vk: convert blue noise texture to 3D

Note: not tested
This commit is contained in:
Ivan Avdeev 2023-10-30 13:43:09 -04:00
parent 1f043a90a6
commit df3c0e30ba
12 changed files with 141 additions and 115 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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,
};

View File

@ -14,6 +14,9 @@
#include "ktx2.h"
#define PCG_IMPLEMENT
#include "pcg.h"
#include <math.h> // 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,
};
}

View File

@ -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 );