From a1cae92a2ccfdda1c54e28d0cfbd85e1a865ec1b Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 11 Sep 2023 12:31:03 -0400 Subject: [PATCH] vk: textures: add blue noise textures They are not used by anything yet. Fallback to generating regular noise textures if bluenoise ones weren't found. Real blue noise textures require Half-Life-PBR repo textures. --- ref/vk/NOTES.md | 13 ++++++ ref/vk/pcg.h | 22 ++++++++++ ref/vk/shaders/bluenoise.glsl | 20 +++++++++ ref/vk/shaders/denoiser.comp | 8 ++++ ref/vk/shaders/ray_interop.h | 3 +- ref/vk/vk_rtx.c | 7 ++- ref/vk/vk_textures.c | 83 +++++++++++++++++++++++++++++++++++ ref/vk/vk_textures.h | 8 ++++ 8 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 ref/vk/pcg.h create mode 100644 ref/vk/shaders/bluenoise.glsl diff --git a/ref/vk/NOTES.md b/ref/vk/NOTES.md index e1029bef..a303a8f1 100644 --- a/ref/vk/NOTES.md +++ b/ref/vk/NOTES.md @@ -398,3 +398,16 @@ TODO: can we not have a BLAS/model for each submodel? Can it be per-model instea # 2023-07-30 - ~~R_DrawStudioModel is the main func for drawing studio model. Called from scene code for each studio entity, with everything current (RI and stuff) set up~~ - `R_StudioDrawModelInternal()` is the main one. It is where it splits into renderer-vs-game rendering functions. + +# 2023-09-11 E293 +- light shaders include structure +- ray_light_direct_{poly,point}.comp + - ray_light_direct.glsl + - utils.glsl + - noise.glsl + - ray_interop.h + - ray_kusochki.glsl + - light.glsl + - brdf.h + - light_common.glsl + - LIGHT_POLYGON: light_polygon.glsl diff --git a/ref/vk/pcg.h b/ref/vk/pcg.h new file mode 100644 index 00000000..1afa488e --- /dev/null +++ b/ref/vk/pcg.h @@ -0,0 +1,22 @@ +// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org +// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) +#pragma once + +#include + +typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t; + +uint32_t pcg32_random_r(pcg32_random_t* rng); + +#if defined(PCG_IMPLEMENT) +uint32_t pcg32_random_r(pcg32_random_t* rng) +{ + uint64_t oldstate = rng->state; + // Advance internal state + rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1); + // Calculate output function (XSH RR), uses old state for max ILP + uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + uint32_t rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} +#endif diff --git a/ref/vk/shaders/bluenoise.glsl b/ref/vk/shaders/bluenoise.glsl new file mode 100644 index 00000000..a25b38fa --- /dev/null +++ b/ref/vk/shaders/bluenoise.glsl @@ -0,0 +1,20 @@ +#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 6 + +// 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); +} + +#endif // ifndef BLUENOISE_H_INCLUDED diff --git a/ref/vk/shaders/denoiser.comp b/ref/vk/shaders/denoiser.comp index 4b143a9c..9bd37860 100644 --- a/ref/vk/shaders/denoiser.comp +++ b/ref/vk/shaders/denoiser.comp @@ -36,6 +36,10 @@ layout(set = 0, binding = 15, rgba16f) uniform image2D prev_temporal_diffuse; 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]; +include "bluenoise.glsl" +#endif const int INDIRECT_SCALE = 2; @@ -185,6 +189,10 @@ void main() { return; } +#ifdef DEBUG_NOISE + imageStore(out_dest, pix, blueNoise(ivec3(pix.xy, ubo.ubo.frame_counter))); return; +#endif + /* if (pix.y < res.y / 3) { */ /* imageStore(out_dest, pix, vec4(pow(float(pix.x) / res.x, 2.2))); return; */ /* } else if (pix.y < res.y * 2 / 3) { */ diff --git a/ref/vk/shaders/ray_interop.h b/ref/vk/shaders/ray_interop.h index 5124b260..1363bb10 100644 --- a/ref/vk/shaders/ray_interop.h +++ b/ref/vk/shaders/ray_interop.h @@ -173,7 +173,8 @@ struct UniformBuffer { mat4 prev_inv_proj, prev_inv_view; float ray_cone_width; uint random_seed; - PAD(2) + uint frame_counter; + PAD(1) }; #undef PAD diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index adaad9e2..a8d871be 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -140,7 +140,7 @@ void VK_RayFrameBegin( void ) { RT_LightsFrameBegin(); } -static void prepareUniformBuffer( const vk_ray_frame_render_args_t *args, int frame_index, float fov_angle_y ) { +static void prepareUniformBuffer( const vk_ray_frame_render_args_t *args, int frame_index, uint32_t frame_counter, float fov_angle_y ) { struct UniformBuffer *ubo = (struct UniformBuffer*)((char*)g_rtx.uniform_buffer.mapped + frame_index * g_rtx.uniform_unit_size); matrix4x4 proj_inv, view_inv; @@ -160,11 +160,13 @@ static void prepareUniformBuffer( const vk_ray_frame_render_args_t *args, int fr ubo->ray_cone_width = atanf((2.0f*tanf(DEG2RAD(fov_angle_y) * 0.5f)) / (float)FRAME_HEIGHT); ubo->random_seed = (uint32_t)gEngine.COM_RandomLong(0, INT32_MAX); + ubo->frame_counter = frame_counter; } typedef struct { const vk_ray_frame_render_args_t* render_args; int frame_index; + uint32_t frame_counter; float fov_angle_y; const vk_lights_bindings_t *light_bindings; } perform_tracing_args_t; @@ -274,7 +276,7 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a // TODO move this to "TLAS producer" g_rtx.res[ExternalResource_tlas].resource = RT_VkAccelPrepareTlas(combuf); - prepareUniformBuffer(args->render_args, args->frame_index, args->fov_angle_y); + prepareUniformBuffer(args->render_args, args->frame_index, args->frame_counter, args->fov_angle_y); { // FIXME this should be done automatically inside meatpipe, TODO //const uint32_t size = sizeof(struct Lights); @@ -567,6 +569,7 @@ void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args) const perform_tracing_args_t trace_args = { .render_args = args, .frame_index = (g_rtx.frame_number % 2), + .frame_counter = g_rtx.frame_number, .fov_angle_y = args->fov_angle_y, .light_bindings = &light_bindings, }; diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index 6c47bcd1..17207f3d 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -15,6 +15,9 @@ #include "com_strings.h" #include "eiface.h" +#define PCG_IMPLEMENT +#include "pcg.h" + #include #include @@ -262,6 +265,84 @@ static void VK_ProcessImage( vk_texture_t *tex, rgbdata_t *pic ) static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap); +static int VK_LoadTextureF(int flags, const char *fmt, ...) { + int tex_id = 0; + char buffer[1024]; + va_list argptr; + va_start( argptr, fmt ); + vsnprintf( buffer, sizeof buffer, fmt, argptr ); + va_end( argptr ); + + return VK_LoadTexture(buffer, NULL, 0, flags); +} + +#define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png" + +static qboolean 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 = VK_LoadTextureInternal(name, &pic, TF_NOMIPMAP); + ASSERT(texid > 0); + + if (blueNoiseTexturesBegin == -1) { + ASSERT(texid == BLUE_NOISE_TEXTURE_ID); + blueNoiseTexturesBegin = texid; + } else { + ASSERT(blueNoiseTexturesBegin + i == texid); + } + } + + return true; +} + +static qboolean loadBlueNoiseTextures(void) { + int blueNoiseTexturesBegin = -1; + for (int i = 0; i < 64; ++i) { + const int texid = VK_LoadTextureF(TF_NOMIPMAP, 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"); + return generateFallbackNoiseTextures(); + } + + blueNoiseTexturesBegin = texid; + } else { + ASSERT(texid > 0); + ASSERT(blueNoiseTexturesBegin + i == texid); + } + } + + INFO("Base blue noise texture is %d", blueNoiseTexturesBegin); + ASSERT(blueNoiseTexturesBegin == BLUE_NOISE_TEXTURE_ID); + + return true; +} + static void VK_CreateInternalTextures( void ) { int dx2, dy, d; @@ -338,6 +419,8 @@ static void VK_CreateInternalTextures( void ) uploadTexture( &tglob.cubemap_placeholder, sides, 6, true ); } + + loadBlueNoiseTextures(); } static VkFormat VK_GetFormat(pixformat_t format) { diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index 050a0adf..66c766b5 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -41,6 +41,14 @@ 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 6 + +// Hardcode blue noise texture size to 64x64x64 +#define BLUE_NOISE_SIZE 64 + qboolean fCustomSkybox; // TODO do we need this for anything? vk_texture_t skybox_cube;