diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index 0eab75ca..de253173 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -3,6 +3,9 @@ - [x] explicitly free default textures; and complain about any leftovers - [x] use the new hash table in materials too, remove dummy textures - [ ] restore blue noise + - [x] vk_texture_t blue_noise; 3d texture + - [x] separate binding similar to skybox in vk_rtx.c and shaders + - [x] patch shader function - [x] why are there references to \*unused - [ ] massage texture code - [ ] single return/goto cleanup diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 65b7a879..9b582c46 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -9,9 +9,6 @@ #include "profiler.h" #include "unordered_roadmap.h" -#define PCG_IMPLEMENT -#include "pcg.h" - #include "xash3d_mathlib.h" #include "crtlib.h" #include "crclib.h" // COM_HashKey @@ -227,70 +224,6 @@ static rgbdata_t *Common_FakeImage( int width, int height, int depth, int flags return r_image; } -#define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png" - -static void generateFallbackNoiseTextures(void) { - pcg32_random_t pcg_state = { - BLUE_NOISE_SIZE * BLUE_NOISE_SIZE - 1, - 17, - }; - uint32_t scratch[BLUE_NOISE_SIZE * BLUE_NOISE_SIZE]; - rgbdata_t pic = { - .width = BLUE_NOISE_SIZE, - .height = BLUE_NOISE_SIZE, - .depth = 1, - .flags = 0, - .type = PF_RGBA_32, - .size = BLUE_NOISE_SIZE * BLUE_NOISE_SIZE * 4, - .buffer = (byte*)&scratch, - .palette = NULL, - .numMips = 1, - .encode = 0, - }; - - int blueNoiseTexturesBegin = -1; - for (int i = 0; i < BLUE_NOISE_SIZE; ++i) { - for (int j = 0; j < COUNTOF(scratch); ++j) { - scratch[j] = pcg32_random_r(&pcg_state); - } - - char name[256]; - snprintf(name, sizeof(name), BLUE_NOISE_NAME_F, i); - const int texid = R_TextureUploadFromBufferNew(name, &pic, TF_NOMIPMAP); - ASSERT(texid > 0); - - if (blueNoiseTexturesBegin == -1) { - ASSERT(texid == BLUE_NOISE_TEXTURE_ID); - blueNoiseTexturesBegin = texid; - } else { - ASSERT(blueNoiseTexturesBegin + i == texid); - } - } -} - -static void loadBlueNoiseTextures(void) { - int blueNoiseTexturesBegin = -1; - for (int i = 0; i < 64; ++i) { - const int texid = textureLoadFromFileF(TF_NOMIPMAP, kColorspaceLinear, BLUE_NOISE_NAME_F, i); - - if (blueNoiseTexturesBegin == -1) { - if (texid <= 0) { - ERR("Couldn't find precomputed blue noise textures. Generating bad quality regular noise textures as a fallback"); - generateFallbackNoiseTextures(); - return; - } - - blueNoiseTexturesBegin = texid; - } else { - ASSERT(texid > 0); - ASSERT(blueNoiseTexturesBegin + i == texid); - } - } - - INFO("Base blue noise texture is %d", blueNoiseTexturesBegin); - ASSERT(blueNoiseTexturesBegin == BLUE_NOISE_TEXTURE_ID); -} - static void createDefaultTextures( void ) { int dx2, dy, d; @@ -367,8 +300,6 @@ static void createDefaultTextures( void ) R_VkTexturesSkyboxUpload( "skybox_placeholder", sides, kColorspaceGamma, true ); } - - // FIXME convert to 3d texture loadBlueNoiseTextures(); } static void destroyDefaultTextures( void ) { @@ -500,7 +431,7 @@ size_t CalcImageSize( pixformat_t format, int width, int height, int depth ) { return size; } -int CalcMipmapCount( int width, int height, uint32_t flags, qboolean haveBuffer ) +int CalcMipmapCount( int width, int height, int depth, uint32_t flags, qboolean haveBuffer ) { int mipcount; @@ -516,7 +447,8 @@ int CalcMipmapCount( int width, int height, uint32_t flags, qboolean haveBuffer { const int mip_width = Q_max( 1, ( width >> mipcount )); const int mip_height = Q_max( 1, ( height >> mipcount )); - if( mip_width == 1 && mip_height == 1 ) + const int mip_depth = Q_max( 1, ( depth >> mipcount )); + if( mip_width == 1 && mip_height == 1 && mip_depth == 1 ) break; } @@ -622,9 +554,13 @@ qboolean validatePicLayers(const char* const name, rgbdata_t *const *const layer return false; } - if (layers[0]->width != layers[i]->width || layers[0]->height != layers[i]->height) { - ERR("Texture %s layer %d has resolution %dx%d inconsistent with layer 0 resolution %dx%d", - name, i, layers[i]->width, layers[i]->height, layers[0]->width, layers[0]->height); + if (layers[0]->width != layers[i]->width + || layers[0]->height != layers[i]->height + || layers[0]->depth != layers[i]->depth) { + ERR("Texture %s layer %d has resolution %dx%d%d inconsistent with layer 0 resolution %dx%dx%d", + name, i, + layers[i]->width, layers[i]->height, layers[i]->depth, + layers[0]->width, layers[0]->height, layers[0]->depth); return false; } diff --git a/ref/vk/r_textures.h b/ref/vk/r_textures.h index 8aa66b2c..51a4b9b7 100644 --- a/ref/vk/r_textures.h +++ b/ref/vk/r_textures.h @@ -21,21 +21,8 @@ typedef struct vk_textures_global_s int dlightTexture; // custom dlight texture int cinTexture; // cinematic texture -// Hardcoded expected blue noise texture slot -// TODO consider moving it into a separate resource bindable by request -// TODO make it a 3D texture. Currently it's just a sequence of BLUE_NOISE_SIZE textures, loaded into consecutive slots. -#define BLUE_NOISE_TEXTURE_ID 7 - -// Hardcode blue noise texture size to 64x64x64 -#define BLUE_NOISE_SIZE 64 - // TODO wire it up for ref_interface_t return qboolean fCustomSkybox; - - // TODO: - //1. vk_texture_t blue_noise; 3d texture - //2. separate binding similar to skybox in vk_rtx.c and shaders - //3. patch shader function } vk_textures_global_t; // TODO rename this consistently diff --git a/ref/vk/shaders/bluenoise.glsl b/ref/vk/shaders/bluenoise.glsl index be468345..531bf227 100644 --- a/ref/vk/shaders/bluenoise.glsl +++ b/ref/vk/shaders/bluenoise.glsl @@ -1,20 +1,15 @@ #ifndef BLUENOISE_H_INCLUDED #define BLUENOISE_H_INCLUDED -// Depends on uniform sampler2D textures[MAX_TEXTURES] binding being defined - -// This is the same hardcoded value as in vk_textures.h -// Please keep them in sync onegai uwu -// TODO: -// - make bluenoise texture a separate binding, not part of textures[] array -// - make it a 3D texture -#define BLUE_NOISE_TEXTURE_BEGIN 7 +// Depends on uniform sampler3D blue_noise_texture binding being defined // Also see vk_textures.h, keep in sync, etc etc #define BLUE_NOISE_SIZE 64 + vec4 blueNoise(ivec3 v) { - v %= BLUE_NOISE_SIZE; - return texelFetch(textures[BLUE_NOISE_TEXTURE_BEGIN+v.z], v.xy, 0); + ivec3 size = textureSize(blue_noise_texture, 0); + v %= size.z; + return texelFetch(blue_noise_texture, v.xy % size.xy, 0); } #endif // ifndef BLUENOISE_H_INCLUDED diff --git a/ref/vk/shaders/denoiser.comp b/ref/vk/shaders/denoiser.comp index 0a16933a..879f4b88 100644 --- a/ref/vk/shaders/denoiser.comp +++ b/ref/vk/shaders/denoiser.comp @@ -37,7 +37,7 @@ layout(set = 0, binding = 16, rgba16f) uniform image2D out_temporal_specular; layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_specular; #ifdef DEBUG_NOISE -layout(set = 0, binding = 18) uniform sampler2D textures[MAX_TEXTURES]; +layout(set = 0, binding = 18) uniform sampler3D blue_noise_texture; include "bluenoise.glsl" #endif diff --git a/ref/vk/vk_framectl.c b/ref/vk/vk_framectl.c index f0115d4c..956e1a39 100644 --- a/ref/vk/vk_framectl.c +++ b/ref/vk/vk_framectl.c @@ -534,6 +534,7 @@ static rgbdata_t *XVK_ReadPixels( void ) { .debug_name = "screenshot", .width = vk_frame.width, .height = vk_frame.height, + .depth = 1, .mips = 1, .layers = 1, .format = dest_format, diff --git a/ref/vk/vk_image.c b/ref/vk/vk_image.c index a18519ee..affdbea4 100644 --- a/ref/vk/vk_image.c +++ b/ref/vk/vk_image.c @@ -30,6 +30,9 @@ r_vk_image_t R_VkImageCreate(const r_vk_image_create_t *create) { VkMemoryRequirements memreq; const qboolean is_cubemap = !!(create->flags & kVkImageFlagIsCubemap); + const qboolean is_3d = create->depth > 1; + + ASSERT(is_cubemap + is_3d != 2); const VkFormat unorm_format = unormFormatFor(create->format); const qboolean create_unorm = @@ -39,10 +42,10 @@ r_vk_image_t R_VkImageCreate(const r_vk_image_create_t *create) { VkImageCreateInfo ici = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, + .imageType = is_3d ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, .extent.width = create->width, .extent.height = create->height, - .extent.depth = 1, + .extent.depth = create->depth, .mipLevels = create->mips, .arrayLayers = create->layers, .format = create->format, @@ -72,7 +75,7 @@ r_vk_image_t R_VkImageCreate(const r_vk_image_create_t *create) { VkImageViewCreateInfo ivci = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .viewType = is_cubemap ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D, + .viewType = is_cubemap ? VK_IMAGE_VIEW_TYPE_CUBE : (is_3d ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D), .format = ici.format, .image = image.image, .subresourceRange.aspectMask = is_depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT, @@ -98,6 +101,7 @@ r_vk_image_t R_VkImageCreate(const r_vk_image_create_t *create) { image.width = create->width; image.height = create->height; + image.depth = create->depth; image.mips = create->mips; image.layers = create->layers; image.flags = create->flags; @@ -252,6 +256,7 @@ void R_VkImageUploadBegin( r_vk_image_t *img ) { void R_VkImageUploadSlice( r_vk_image_t *img, int layer, int mip, int size, const void *data ) { const uint32_t width = Q_max(1, img->width >> mip); const uint32_t height = Q_max(1, img->height >> mip); + const uint32_t depth = Q_max(1, img->depth >> mip); const uint32_t texel_block_size = R_VkImageFormatTexelBlockSize(img->format); const vk_staging_image_args_t staging_args = { @@ -269,7 +274,7 @@ void R_VkImageUploadSlice( r_vk_image_t *img, int layer, int mip, int size, cons .imageExtent = (VkExtent3D){ .width = width, .height = height, - .depth = 1, + .depth = depth, }, }, .layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/ref/vk/vk_image.h b/ref/vk/vk_image.h index 7e361310..55da9f92 100644 --- a/ref/vk/vk_image.h +++ b/ref/vk/vk_image.h @@ -11,7 +11,7 @@ typedef struct r_vk_image_s { // Used for sRGB-γ-unaware traditional renderer VkImageView view_unorm; - uint32_t width, height; + uint32_t width, height, depth; int mips, layers; VkFormat format; uint32_t flags; @@ -25,7 +25,7 @@ enum { typedef struct { const char *debug_name; - uint32_t width, height; + uint32_t width, height, depth; int mips, layers; VkFormat format; VkImageTiling tiling; diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index b756c4d3..2fd7a31a 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -53,7 +53,8 @@ X(Buffer, lights) \ X(Buffer, light_grid) \ X(Texture, textures) \ - X(Texture, skybox) + X(Texture, skybox) \ + X(Texture, blue_noise_texture) enum { #define RES_ENUM(type, name) ExternalResource_##name, @@ -123,7 +124,15 @@ void VK_RayNewMap( void ) { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // FIXME we should pick tglob.dii_all_textures here directly .value = (vk_descriptor_value_t){ - .image = R_VkTextureGetSkyboxDescriptorImageInfo(), + .image = R_VkTexturesGetSkyboxDescriptorImageInfo(), + }, + }; + + g_rtx.res[ExternalResource_blue_noise_texture].resource = (vk_resource_t){ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + // FIXME we should pick tglob.dii_all_textures here directly + .value = (vk_descriptor_value_t){ + .image = R_VkTexturesGetBlueNoiseImageInfo(), }, }; } @@ -425,6 +434,7 @@ static void reloadMainpipe(void) { .debug_name = mr->name, .width = FRAME_WIDTH, .height = FRAME_HEIGHT, + .depth = 1, .mips = 1, .layers = 1, .format = mr->image_format, diff --git a/ref/vk/vk_swapchain.c b/ref/vk/vk_swapchain.c index d3994ce5..ca8a68bc 100644 --- a/ref/vk/vk_swapchain.c +++ b/ref/vk/vk_swapchain.c @@ -41,6 +41,7 @@ static void createDepthImage(int w, int h, VkFormat depth_format) { .layers = 1, .width = w, .height = h, + .depth = 1, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, }; diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index fe8ff3b6..e90d4c38 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -14,6 +14,9 @@ #include "ktx2.h" +#define PCG_IMPLEMENT +#include "pcg.h" + #include // sqrt #define LOG_MODULE LogModule_Textures @@ -42,20 +45,86 @@ static struct { vk_texture_t skybox_cube; vk_texture_t cubemap_placeholder; + + vk_texture_t blue_noise; } g_vktextures; -#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) - +// Exported from r_textures.h size_t CalcImageSize( pixformat_t format, int width, int height, int depth ); -int CalcMipmapCount( int width, int height, uint32_t flags, qboolean haveBuffer ); +int CalcMipmapCount( int width, int height, int depth, uint32_t flags, qboolean haveBuffer ); qboolean validatePicLayers(const char* const name, rgbdata_t *const *const layers, int num_layers); void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, int flags ); static VkSampler pickSamplerForFlags( texFlags_t flags ); +static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint); // FIXME should be static void unloadSkybox( void ); +// Hardcode blue noise texture size to 64x64x64 +#define BLUE_NOISE_SIZE 64 +#define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png" + +static void generateFallbackNoiseTextures(void) { + const size_t blue_noise_count = BLUE_NOISE_SIZE * BLUE_NOISE_SIZE * BLUE_NOISE_SIZE; + const size_t blue_noise_size = blue_noise_count * sizeof(uint32_t); + uint32_t *const scratch = Mem_Malloc(vk_core.pool /* TODO textures pool */, blue_noise_size); + rgbdata_t pic = { + .width = BLUE_NOISE_SIZE, + .height = BLUE_NOISE_SIZE, + .depth = BLUE_NOISE_SIZE, + .flags = TF_NOMIPMAP, + .type = PF_RGBA_32, + .size = blue_noise_size, + .buffer = (byte*)scratch, + .palette = NULL, + .numMips = 1, + .encode = 0, + }; + + ERR("Generating bad quality regular noise textures as a fallback for blue noise textures"); + + // Fill with random data + { + pcg32_random_t pcg_state = { blue_noise_count - 1, 17 }; + for (int j = 0; j < blue_noise_count; ++j) + scratch[j] = pcg32_random_r(&pcg_state); + } + + static const char *const name = "bluenoise/pcg_placeholder_fixme"; + const qboolean is_cubemap = false; + rgbdata_t *pica[1] = {&pic}; + ASSERT(uploadTexture(-1, &g_vktextures.blue_noise, pica, 1, is_cubemap, kColorspaceLinear)); + Mem_Free(scratch); +} + +static void loadBlueNoiseTextures(void) { + generateFallbackNoiseTextures(); + +#if 0 // TODO + int blueNoiseTexturesBegin = -1; + for (int i = 0; i < 64; ++i) { + const int texid = textureLoadFromFileF(TF_NOMIPMAP, kColorspaceLinear, BLUE_NOISE_NAME_F, i); + + if (blueNoiseTexturesBegin == -1) { + if (texid <= 0) { + ERR("Couldn't find precomputed blue noise textures. Generating bad quality regular noise textures as a fallback"); + generateFallbackNoiseTextures(); + return; + } + + blueNoiseTexturesBegin = texid; + } else { + ASSERT(texid > 0); + ASSERT(blueNoiseTexturesBegin + i == texid); + } + } + + INFO("Base blue noise texture is %d", blueNoiseTexturesBegin); + ASSERT(blueNoiseTexturesBegin == BLUE_NOISE_TEXTURE_ID); +#endif +} + qboolean R_VkTexturesInit( void ) { R_SPEEDS_METRIC(g_vktextures.stats.count, "count", kSpeedsMetricCount); R_SPEEDS_METRIC(g_vktextures.stats.size_total, "size_total", kSpeedsMetricBytes); @@ -92,6 +161,8 @@ qboolean R_VkTexturesInit( void ) { } } + loadBlueNoiseTextures(); + return true; } @@ -306,6 +377,7 @@ static qboolean uploadRawKtx2( int tex_index, vk_texture_t *tex, const rgbdata_t .debug_name = TEX_NAME(tex), .width = header->pixelWidth, .height = header->pixelHeight, + .depth = header->pixelDepth, .mips = header->levelCount, .layers = 1, // TODO or 6 for cubemap; header->faceCount .format = header->vkFormat, @@ -382,9 +454,10 @@ static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *co } else { const int width = layers[0]->width; const int height = layers[0]->height; + const int depth = layers[0]->depth; const qboolean compute_mips = layers[0]->type == PF_RGBA_32 && layers[0]->numMips < 2; const VkFormat format = VK_GetFormat(layers[0]->type, colorspace_hint); - const int mipCount = compute_mips ? CalcMipmapCount( width, height, tex->flags, true ) : layers[0]->numMips; + const int mipCount = compute_mips ? CalcMipmapCount( width, height, depth, tex->flags, true ) : layers[0]->numMips; if (format == VK_FORMAT_UNDEFINED) { ERR("Unsupported PF format %d", layers[0]->type); @@ -405,6 +478,7 @@ static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *co .debug_name = TEX_NAME(tex), .width = width, .height = height, + .depth = depth, .mips = mipCount, .layers = num_layers, .format = format, @@ -422,6 +496,7 @@ static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *co tex->width = width; tex->height = height; + tex->depth = depth; { R_VkImageUploadBegin(&tex->vk.image); @@ -433,7 +508,8 @@ static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *co for (int mip = 0; mip < mipCount; ++mip) { const int width = Q_max( 1, ( pic->width >> mip )); const int height = Q_max( 1, ( pic->height >> mip )); - const size_t mip_size = CalcImageSize( pic->type, width, height, 1 ); + const int depth = Q_max( 1, ( pic->depth >> mip )); + const size_t mip_size = CalcImageSize( pic->type, width, height, depth ); R_VkImageUploadSlice(&tex->vk.image, layer, mip, mip_size, buf); tex->total_size += mip_size; @@ -441,7 +517,7 @@ static qboolean uploadTexture(int index, vk_texture_t *tex, rgbdata_t *const *co // Build mip in place for the next mip level if (compute_mips) { if ( mip < mipCount - 1 ) - BuildMipMap( buf, width, height, 1, tex->flags ); + BuildMipMap( buf, width, height, depth, tex->flags ); } else { buf += mip_size; } @@ -493,7 +569,7 @@ void unloadSkybox( void ) { tglob.fCustomSkybox = false; } -VkDescriptorImageInfo R_VkTextureGetSkyboxDescriptorImageInfo( void ) { +VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( void ) { return (VkDescriptorImageInfo){ .sampler = g_vktextures.default_sampler, .imageView = g_vktextures.skybox_cube.vk.image.view @@ -520,3 +596,11 @@ VkDescriptorSet R_VkTextureGetDescriptorUnorm( uint index ) { const VkDescriptorImageInfo* R_VkTexturesGetAllDescriptorsArray( void ) { return g_vktextures.dii_all_textures; } + +VkDescriptorImageInfo R_VkTexturesGetBlueNoiseImageInfo( void ) { + return (VkDescriptorImageInfo) { + .sampler = g_vktextures.default_sampler, + .imageView = g_vktextures.blue_noise.vk.image.view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; +} diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index 5f6f07e6..419f4302 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -11,12 +11,14 @@ typedef struct vk_texture_s { urmom_header_t hdr_; - int width, height; + int width, height, depth; uint32_t flags; int total_size; struct { r_vk_image_t image; + + // TODO external table VkDescriptorSet descriptor_unorm; } vk; @@ -37,6 +39,8 @@ qboolean R_VkTexturesSkyboxUpload( const char *name, rgbdata_t *const sides[6], qboolean R_VkTextureUpload(int index, vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, colorspace_hint_e colorspace_hint); void R_VkTextureDestroy(int index, vk_texture_t *tex); -VkDescriptorImageInfo R_VkTextureGetSkyboxDescriptorImageInfo( void ); +VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( void ); const VkDescriptorImageInfo* R_VkTexturesGetAllDescriptorsArray( void ); VkDescriptorSet R_VkTextureGetDescriptorUnorm( uint index ); + +VkDescriptorImageInfo R_VkTexturesGetBlueNoiseImageInfo( void );