Skip to content

Commit 3ded56e

Browse files
authored
feat(vulkan): RT demo multi-sample rays and scene composite
Adds r_rtxSamples, color buffer binding for r_rtxComposite blending, UBO traceParams, barriers, and refreshed rtx_demo SPIR-V.
1 parent 8241198 commit 3ded56e

9 files changed

Lines changed: 1363 additions & 803 deletions

File tree

src/renderers/vulkan/shaders/glsl/rtx_demo.rchit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ layout(set = 0, binding = 2, std140) uniform RtxFrame {
88
vec4 viewOrigin;
99
vec4 zNearFar;
1010
vec4 outputSize; /* xy = resolution; z = r_rtx mode (1-3); w = r_rtxComposite blend */
11+
vec4 traceParams;
1112
} rtx;
1213

1314
void main()

src/renderers/vulkan/shaders/glsl/rtx_demo.rgen

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,29 @@ layout(set = 0, binding = 2, std140) uniform RtxFrame {
1010
mat4 invViewProj;
1111
vec4 viewOrigin;
1212
vec4 zNearFar; /* x=zNear, y=zFar */
13-
vec4 outputSize; /* xy = width,height; z = r_rtx mode 0-3 (hit shader demo tint); w unused in rgen */
13+
vec4 outputSize; /* xy = width,height; z = r_rtx mode; w = r_rtxComposite blend */
14+
vec4 traceParams; /* x = r_rtxSamples */
1415
} rtx;
1516

1617
layout(set = 0, binding = 3) uniform sampler2D depthTex;
18+
layout(set = 0, binding = 4) uniform sampler2D colorTex;
1719

18-
void main()
20+
vec2 sampleOffset( int index )
21+
{
22+
switch ( index & 7 ) {
23+
case 0: return vec2( 0.5, 0.5 );
24+
case 1: return vec2( 0.25, 0.75 );
25+
case 2: return vec2( 0.75, 0.25 );
26+
case 3: return vec2( 0.125, 0.375 );
27+
case 4: return vec2( 0.625, 0.875 );
28+
case 5: return vec2( 0.375, 0.125 );
29+
case 6: return vec2( 0.875, 0.625 );
30+
default: return vec2( 0.0625, 0.5625 );
31+
}
32+
}
33+
34+
vec3 tracePrimary( vec2 uv )
1935
{
20-
vec2 pix = vec2( gl_LaunchIDEXT.xy ) + vec2( 0.5 );
21-
vec2 uv = pix / max( rtx.outputSize.xy, vec2( 1.0 ) );
2236
float d = textureLod( depthTex, uv, 0.0 ).r;
2337

2438
vec3 ro = rtx.viewOrigin.xyz;
@@ -37,6 +51,25 @@ void main()
3751
rd = normalize( worldPos.xyz - ro );
3852
}
3953

54+
hitValue = vec3( 0.0 );
4055
traceRayEXT( topLevelAS, gl_RayFlagsOpaqueEXT, 0xFFu, 0u, 0u, 0u, ro, 0.01, rd, 1.0e5, 0 );
41-
imageStore( outImage, ivec2( gl_LaunchIDEXT.xy ), vec4( hitValue, 1.0 ) );
56+
return hitValue;
57+
}
58+
59+
void main()
60+
{
61+
vec2 extent = max( rtx.outputSize.xy, vec2( 1.0 ) );
62+
vec2 baseUv = ( vec2( gl_LaunchIDEXT.xy ) + vec2( 0.5 ) ) / extent;
63+
vec3 traced = vec3( 0.0 );
64+
int samples = clamp( int( floor( rtx.traceParams.x + 0.5 ) ), 1, 8 );
65+
66+
for ( int i = 0; i < samples; ++i ) {
67+
vec2 uv = ( vec2( gl_LaunchIDEXT.xy ) + sampleOffset( i ) ) / extent;
68+
traced += tracePrimary( clamp( uv, vec2( 0.0 ), vec2( 1.0 ) ) );
69+
}
70+
traced /= float( samples );
71+
72+
vec3 sceneColor = textureLod( colorTex, baseUv, 0.0 ).rgb;
73+
float blend = clamp( rtx.outputSize.w, 0.0, 1.0 );
74+
imageStore( outImage, ivec2( gl_LaunchIDEXT.xy ), vec4( mix( traced, sceneColor, blend ), 1.0 ) );
4275
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
glslang_validator_path=/usr/bin/glslangValidator
2-
glslang_validator_version=Glslang Version: 11:15.1.0
3-
generated_at=2026-05-04T04:58:26Z
4-
shader_data_sha256=571bf409256f64df94df8e481451a75f96532552e6d19bfb8e61447e41213b02
2+
glslang_validator_version=Glslang Version: 11:16.2.0
3+
generated_at=2026-05-05T02:17:24Z
4+
shader_data_sha256=a95900318cc999da89888be25b38ecb1651bda14a46f89404623c108c8e4b880
55
shader_binding_sha256=80ccc301b0a921c37979d0ba06adf3a4f64fce5674c5990c68928c56764d413d

src/renderers/vulkan/shaders/spirv/generated/shader_data.c

Lines changed: 409 additions & 261 deletions
Large diffs are not rendered by default.

src/renderers/vulkan/shaders/spirv/shader_data.c

Lines changed: 409 additions & 261 deletions
Large diffs are not rendered by default.

src/renderers/vulkan/tr_init.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ cvar_t *r_rtx;
201201
cvar_t *r_rtxDemo;
202202
cvar_t *r_rtxWorldPrimCap;
203203
cvar_t *r_rtxComposite;
204+
cvar_t *r_rtxSamples;
204205
cvar_t *r_forwardPlus;
205206
cvar_t *r_forwardPlusMaxPerTile;
206207
cvar_t *r_forwardPlusDebug;
@@ -3495,9 +3496,13 @@ static void R_Register( void )
34953496
r_rtxComposite = ri.Cvar_Get( "r_rtxComposite", "0", CVAR_ARCHIVE_ND );
34963497
ri.Cvar_CheckRange( r_rtxComposite, "0", "1", CV_FLOAT );
34973498
ri.Cvar_SetDescription( r_rtxComposite,
3498-
"When USE_VULKAN_RTX and \\r_rtxDemo 1: blend resolved **raster** HDR color into the RT demo output per pixel: "
3499-
"out = mix(rtHit, rasterColor, factor). **0** = RT colors only (legacy demo). **0.20.5** approximates hybrid paths (e.g. Quake II RTX-style grounding). Requires RT demo composite." );
3499+
"When USE_VULKAN_RTX and \\r_rtxDemo 1: blend resolved raster HDR color into the RT output per pixel. "
3500+
"0 = traced color only; 1 = resolved scene color only; 0.2 to 0.5 keeps traced grounding with scene detail." );
35003501
ri.Cvar_SetGroup( r_rtxComposite, CVG_RENDERER );
3502+
r_rtxSamples = ri.Cvar_Get( "r_rtxSamples", "1", CVAR_ARCHIVE_ND );
3503+
ri.Cvar_CheckRange( r_rtxSamples, "1", "8", CV_INTEGER );
3504+
ri.Cvar_SetDescription( r_rtxSamples, "Primary ray samples per pixel for the Vulkan RT output. Higher values smooth edge shimmer at extra GPU cost." );
3505+
ri.Cvar_SetGroup( r_rtxSamples, CVG_RENDERER );
35013506
r_forwardPlus = ri.Cvar_Get( "r_forwardPlus", "1", CVAR_ARCHIVE_ND | CVAR_LATCH );
35023507
ri.Cvar_CheckRange( r_forwardPlus, "0", "1", CV_INTEGER );
35033508
ri.Cvar_SetDescription( r_forwardPlus, "Forward+ (default 1 on Vulkan): device-local light SSBO + per-tile cull compute (16px tiles; max from \\r_forwardPlusMaxPerTile, default 8). Packs at most MAX_DLIGHTS (32) for tess.dlightBits. PBR: \\r_forwardPlusDebug, \\r_forwardPlusShade. Set 0 to disable (vid_restart). See docs/RENDERER_2026_ARCHITECTURE_PASS.md." );

src/renderers/vulkan/tr_local.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,7 @@ extern cvar_t *r_rtx;
15621562
extern cvar_t *r_rtxDemo;
15631563
extern cvar_t *r_rtxWorldPrimCap;
15641564
extern cvar_t *r_rtxComposite;
1565+
extern cvar_t *r_rtxSamples;
15651566
extern cvar_t *r_forwardPlus;
15661567
extern cvar_t *r_forwardPlusMaxPerTile;
15671568
extern cvar_t *r_forwardPlusDebug;

src/renderers/vulkan/vk_rtx.c

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ typedef struct {
2323
float invViewProj[16];
2424
float viewOrigin[4];
2525
float zNearFar[4];
26-
/* xy = RT output resolution (pixels); z = latched r_rtx mode 0-3 for demo hit tint; w unused */
26+
/* xy = RT output resolution; z = r_rtx mode; w = r_rtxComposite blend */
2727
float outputSize[4];
28+
/* x = r_rtxSamples; yzw reserved */
29+
float traceParams[4];
2830
} VkRtxFrameUBO_t;
2931

3032
static struct {
@@ -236,6 +238,37 @@ static void vk_rtx_create_rt_output( uint32_t w, uint32_t h, VkDescriptorSet des
236238
rtx.rt_image_traced = qfalse;
237239
}
238240

241+
static void vk_rtx_update_color_descriptor( void )
242+
{
243+
VkDescriptorImageInfo colorInfo;
244+
VkWriteDescriptorSet write;
245+
Vk_Sampler_Def sd;
246+
247+
if ( rtx.descriptor_set == VK_NULL_HANDLE || vk.color_image_view == VK_NULL_HANDLE ) {
248+
return;
249+
}
250+
251+
Com_Memset( &sd, 0, sizeof( sd ) );
252+
sd.gl_mag_filter = sd.gl_min_filter = GL_LINEAR;
253+
sd.address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
254+
sd.max_lod_1_0 = qtrue;
255+
sd.noAnisotropy = qtrue;
256+
257+
Com_Memset( &colorInfo, 0, sizeof( colorInfo ) );
258+
colorInfo.sampler = vk_find_sampler( &sd );
259+
colorInfo.imageView = vk.color_image_view;
260+
colorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
261+
262+
Com_Memset( &write, 0, sizeof( write ) );
263+
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
264+
write.dstSet = rtx.descriptor_set;
265+
write.dstBinding = 4;
266+
write.descriptorCount = 1;
267+
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
268+
write.pImageInfo = &colorInfo;
269+
qvkUpdateDescriptorSets( vk.device, 1, &write, 0, NULL );
270+
}
271+
239272
static void vk_rtx_rebuild_world_blas( void )
240273
{
241274
VkAccelerationStructureCreateInfoKHR asci;
@@ -573,7 +606,7 @@ void vk_rtx_init( void )
573606
{
574607
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rtProps;
575608
VkPhysicalDeviceProperties2 props2;
576-
VkDescriptorSetLayoutBinding bindings[4];
609+
VkDescriptorSetLayoutBinding bindings[5];
577610
VkDescriptorSetLayoutCreateInfo dslci;
578611
VkDescriptorPoolSize poolSizes[4];
579612
VkDescriptorPoolCreateInfo pci;
@@ -596,6 +629,7 @@ void vk_rtx_init( void )
596629
uint8_t *sbtHost;
597630
size_t hbufSize;
598631
int gi;
632+
int sampleCount;
599633
VkResult pipeRes;
600634

601635
vk_rtx_shutdown();
@@ -648,10 +682,14 @@ void vk_rtx_init( void )
648682
bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
649683
bindings[3].descriptorCount = 1;
650684
bindings[3].stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
685+
bindings[4].binding = 4;
686+
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
687+
bindings[4].descriptorCount = 1;
688+
bindings[4].stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
651689

652690
Com_Memset( &dslci, 0, sizeof( dslci ) );
653691
dslci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
654-
dslci.bindingCount = 4;
692+
dslci.bindingCount = 5;
655693
dslci.pBindings = bindings;
656694
VK_CHECK( qvkCreateDescriptorSetLayout( vk.device, &dslci, NULL, &rtx.dsl ) );
657695

@@ -662,7 +700,7 @@ void vk_rtx_init( void )
662700
poolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
663701
poolSizes[2].descriptorCount = 1;
664702
poolSizes[3].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
665-
poolSizes[3].descriptorCount = 1;
703+
poolSizes[3].descriptorCount = 2;
666704
Com_Memset( &pci, 0, sizeof( pci ) );
667705
pci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
668706
pci.maxSets = 1;
@@ -732,6 +770,7 @@ void vk_rtx_init( void )
732770
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
733771
writes[1].pImageInfo = &depthInfo;
734772
qvkUpdateDescriptorSets( vk.device, 2, writes, 0, NULL );
773+
vk_rtx_update_color_descriptor();
735774

736775
vk_rtx_rebuild_world_blas();
737776

@@ -810,7 +849,12 @@ void vk_rtx_init( void )
810849
qvkUnmapMemory( vk.device, rtx.sbt_memory );
811850

812851
rtx.ready = qtrue;
813-
ri.Printf( PRINT_ALL, "[VK][RTX] RTX demo ready (r_rtx=%d): world BLAS when map loaded, else fallback triangle; hit tint by r_rtx 1=shadows 2=reflections 3=full; composites after main/post bloom\n", r_rtx->integer );
852+
sampleCount = ( r_rtxSamples && r_rtxSamples->integer > 0 ) ? r_rtxSamples->integer : 1;
853+
if ( sampleCount > 8 ) {
854+
sampleCount = 8;
855+
}
856+
ri.Printf( PRINT_ALL, "[VK][RTX] Ray pass ready (r_rtx=%d, samples=%d, blend=%.2f): world BLAS when map loaded, else fallback triangle\n",
857+
r_rtx->integer, sampleCount, r_rtxComposite ? r_rtxComposite->value : 0.0f );
814858
}
815859

816860
void vk_rtx_frame_begin( void )
@@ -828,6 +872,7 @@ void vk_rtx_frame_begin( void )
828872
}
829873

830874
vk_rtx_create_rt_output( w, h, rtx.descriptor_set );
875+
vk_rtx_update_color_descriptor();
831876
ri.Printf( PRINT_ALL, "[VK][RTX] Resized RT output to %ux%u\n", w, h );
832877
}
833878

@@ -844,6 +889,7 @@ void vk_rtx_record_demo_pass( VkCommandBuffer cmd )
844889
float viewProj[16];
845890
float zNear, zFar;
846891
VkImageAspectFlags depthAspect;
892+
uint32_t preBarrierCount;
847893

848894
if ( !rtx.ready || !cmd || !r_rtxDemo || !r_rtxDemo->integer ) {
849895
return;
@@ -884,7 +930,20 @@ void vk_rtx_record_demo_pass( VkCommandBuffer cmd )
884930
}
885931
frameUbo.outputSize[2] = (float)rtxMode;
886932
}
887-
frameUbo.outputSize[3] = 0.0f;
933+
frameUbo.outputSize[3] = r_rtxComposite ? r_rtxComposite->value : 0.0f;
934+
if ( frameUbo.outputSize[3] < 0.0f ) {
935+
frameUbo.outputSize[3] = 0.0f;
936+
}
937+
if ( frameUbo.outputSize[3] > 1.0f ) {
938+
frameUbo.outputSize[3] = 1.0f;
939+
}
940+
frameUbo.traceParams[0] = ( r_rtxSamples && r_rtxSamples->integer > 0 ) ? (float)r_rtxSamples->integer : 1.0f;
941+
if ( frameUbo.traceParams[0] > 8.0f ) {
942+
frameUbo.traceParams[0] = 8.0f;
943+
}
944+
frameUbo.traceParams[1] = 0.0f;
945+
frameUbo.traceParams[2] = 0.0f;
946+
frameUbo.traceParams[3] = 0.0f;
888947
Com_Memcpy( rtx.rtx_ubo_ptr, &frameUbo, sizeof( frameUbo ) );
889948
}
890949

@@ -938,10 +997,27 @@ void vk_rtx_record_demo_pass( VkCommandBuffer cmd )
938997
barriers[0].srcAccessMask = rtx.rt_image_traced ? VK_ACCESS_SHADER_WRITE_BIT : 0;
939998
barriers[0].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
940999

1000+
preBarrierCount = 1;
1001+
if ( vk.color_image != VK_NULL_HANDLE ) {
1002+
barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1003+
barriers[1].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1004+
barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1005+
barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1006+
barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1007+
barriers[1].image = vk.color_image;
1008+
barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1009+
barriers[1].subresourceRange.levelCount = 1;
1010+
barriers[1].subresourceRange.layerCount = 1;
1011+
barriers[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1012+
barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1013+
preBarrierCount = 2;
1014+
}
1015+
9411016
qvkCmdPipelineBarrier( cmd,
942-
rtx.rt_image_traced ? VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1017+
( rtx.rt_image_traced ? VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ) |
1018+
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
9431019
VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR,
944-
0, 0, NULL, 0, NULL, 1, barriers );
1020+
0, 0, NULL, 0, NULL, preBarrierCount, barriers );
9451021

9461022
qvkCmdBindPipeline( cmd, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtx.pipeline );
9471023
qvkCmdBindDescriptorSets( cmd, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtx.pl, 0, 1, &rtx.descriptor_set, 0, NULL );

0 commit comments

Comments
 (0)