From 930872b35884adb884fc2731788f7e7c35a42a90 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Wed, 23 Dec 2020 23:06:10 +0100 Subject: [PATCH] Add resolution scaling to Vulkan renderer This commit adds a new cvar called vk_pixel_size that represents how big pixels should look in the rendered world in order to simulate lower screen resolutions. With its default value of 1 everything looks normal, but with bigger sizes (e.g. 4) the rendered world starts to look "pixelated" due to pixels appearing bigger. To implement the effect, the viewport and scissor are modified when drawing the world so the rendering results cover a smaller area in the top-left corner of the image. The post-processing fragment shader is used to scale the image back to the swapchain size before drawing UI elements on top of it. The UI is not affected by this change, so the existing UI scaling options continue to work as before with no changes, adding some flexibility to the mix. Related to feature request #588. --- doc/040_cvarlist.md | 6 ++ src/client/refresh/vk/header/local.h | 2 + src/client/refresh/vk/spirv/world_warp_frag.c | 71 ++++++++++--------- src/client/refresh/vk/vk_common.c | 2 + src/client/refresh/vk/vk_pipeline.c | 2 +- src/client/refresh/vk/vk_rmain.c | 43 ++++++++++- stuff/shaders/world_warp.frag | 6 +- 7 files changed, 94 insertions(+), 38 deletions(-) diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index a834f2ec..c7cb090f 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -405,6 +405,12 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable` * **vk_picmip**: Shrink factor for the textures. (default: `0`) +* **vk_pixel_size**: Pixel size when rendering the world, used to simulate + lower screen resolutions. The value represents the length, in pixels, of the + side of each pixel block. For example, with size 2 pixels are 2x2 squares, + and at 1600x1200 the image is effectively an upscaled 800x600 image. + (default: `1`) + * **vk_dynamic**: Use dynamic lighting. (default: `1`) * **vk_showtris**: Display mesh triangles. (default: `0`) diff --git a/src/client/refresh/vk/header/local.h b/src/client/refresh/vk/header/local.h index 5d27c2cd..26fea02f 100644 --- a/src/client/refresh/vk/header/local.h +++ b/src/client/refresh/vk/header/local.h @@ -145,6 +145,7 @@ extern cvar_t *vk_flashblend; extern cvar_t *vk_finish; extern cvar_t *vk_polyblend; extern cvar_t *vk_shadows; +extern cvar_t *vk_pixel_size; extern cvar_t *vk_particle_size; extern cvar_t *vk_particle_att_a; extern cvar_t *vk_particle_att_b; @@ -294,6 +295,7 @@ typedef struct qboolean stereo_enabled; VkPipeline current_pipeline; + qvkrenderpasstype_t current_renderpass; } vkstate_t; extern vkconfig_t vk_config; diff --git a/src/client/refresh/vk/spirv/world_warp_frag.c b/src/client/refresh/vk/spirv/world_warp_frag.c index ea2ac83c..55f87dc3 100644 --- a/src/client/refresh/vk/spirv/world_warp_frag.c +++ b/src/client/refresh/vk/spirv/world_warp_frag.c @@ -1,42 +1,44 @@ - // 8.13.3559 + // 1011.0.0 #pragma once const uint32_t world_warp_frag_spv[] = { - 0x07230203,0x00010000,0x00080008,0x00000078,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000a,0x0000007e,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000000c,0x00000070,0x00030010, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000000c,0x00000076,0x00030010, 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00090004,0x415f4c47,0x735f4252, 0x72617065,0x5f657461,0x64616873,0x6f5f7265,0x63656a62,0x00007374,0x00040005,0x00000004, 0x6e69616d,0x00000000,0x00030005,0x00000009,0x00007675,0x00060005,0x0000000c,0x465f6c67, 0x43676172,0x64726f6f,0x00000000,0x00060005,0x00000012,0x68737550,0x736e6f43,0x746e6174, 0x00000000,0x00050006,0x00000012,0x00000000,0x656d6974,0x00000000,0x00050006,0x00000012, 0x00000001,0x6c616373,0x00000065,0x00060006,0x00000012,0x00000002,0x57726373,0x68746469, - 0x00000000,0x00060006,0x00000012,0x00000003,0x48726373,0x68676965,0x00000074,0x00030005, - 0x00000014,0x00006370,0x00030005,0x0000002c,0x00007873,0x00030005,0x0000003d,0x00007973, - 0x00040005,0x0000004c,0x69685378,0x00007466,0x00040005,0x00000057,0x69685379,0x00007466, - 0x00050005,0x00000060,0x74736964,0x6974726f,0x00006e6f,0x00060005,0x00000070,0x67617266, - 0x746e656d,0x6f6c6f43,0x00000072,0x00050005,0x00000074,0x78655473,0x65727574,0x00000000, - 0x00040047,0x0000000c,0x0000000b,0x0000000f,0x00050048,0x00000012,0x00000000,0x00000023, - 0x00000044,0x00050048,0x00000012,0x00000001,0x00000023,0x00000048,0x00050048,0x00000012, - 0x00000002,0x00000023,0x0000004c,0x00050048,0x00000012,0x00000003,0x00000023,0x00000050, - 0x00030047,0x00000012,0x00000002,0x00040047,0x00000070,0x0000001e,0x00000000,0x00040047, - 0x00000074,0x00000022,0x00000000,0x00040047,0x00000074,0x00000021,0x00000000,0x00020013, - 0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017, - 0x00000007,0x00000006,0x00000002,0x00040020,0x00000008,0x00000007,0x00000007,0x00040017, - 0x0000000a,0x00000006,0x00000004,0x00040020,0x0000000b,0x00000001,0x0000000a,0x0004003b, - 0x0000000b,0x0000000c,0x00000001,0x00040015,0x0000000d,0x00000020,0x00000000,0x0004002b, - 0x0000000d,0x0000000e,0x00000000,0x00040020,0x0000000f,0x00000001,0x00000006,0x0006001e, - 0x00000012,0x00000006,0x00000006,0x00000006,0x00000006,0x00040020,0x00000013,0x00000009, - 0x00000012,0x0004003b,0x00000013,0x00000014,0x00000009,0x00040015,0x00000015,0x00000020, - 0x00000001,0x0004002b,0x00000015,0x00000016,0x00000002,0x00040020,0x00000017,0x00000009, - 0x00000006,0x0004002b,0x0000000d,0x0000001b,0x00000001,0x0004002b,0x00000015,0x0000001e, - 0x00000003,0x0004002b,0x00000015,0x00000023,0x00000000,0x0004002b,0x00000006,0x00000026, - 0x00000000,0x00020014,0x00000027,0x00040020,0x0000002b,0x00000007,0x00000006,0x0004002b, - 0x00000015,0x0000002d,0x00000001,0x0004002b,0x00000006,0x00000032,0x40000000,0x0004002b, - 0x00000006,0x00000052,0x40490e56,0x0004002b,0x00000006,0x00000054,0x41200000,0x0004002b, - 0x00000006,0x0000006a,0x3bda3c21,0x00040020,0x0000006f,0x00000003,0x0000000a,0x0004003b, - 0x0000006f,0x00000070,0x00000003,0x00090019,0x00000071,0x00000006,0x00000001,0x00000000, - 0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000072,0x00000071,0x00040020, - 0x00000073,0x00000000,0x00000072,0x0004003b,0x00000073,0x00000074,0x00000000,0x00050036, + 0x00000000,0x00060006,0x00000012,0x00000003,0x48726373,0x68676965,0x00000074,0x00060006, + 0x00000012,0x00000004,0x65786970,0x7a69536c,0x00000065,0x00030005,0x00000014,0x00006370, + 0x00030005,0x0000002c,0x00007873,0x00030005,0x0000003d,0x00007973,0x00040005,0x0000004c, + 0x69685378,0x00007466,0x00040005,0x00000057,0x69685379,0x00007466,0x00050005,0x00000060, + 0x74736964,0x6974726f,0x00006e6f,0x00060005,0x00000076,0x67617266,0x746e656d,0x6f6c6f43, + 0x00000072,0x00050005,0x0000007a,0x78655473,0x65727574,0x00000000,0x00040047,0x0000000c, + 0x0000000b,0x0000000f,0x00050048,0x00000012,0x00000000,0x00000023,0x00000044,0x00050048, + 0x00000012,0x00000001,0x00000023,0x00000048,0x00050048,0x00000012,0x00000002,0x00000023, + 0x0000004c,0x00050048,0x00000012,0x00000003,0x00000023,0x00000050,0x00050048,0x00000012, + 0x00000004,0x00000023,0x00000054,0x00030047,0x00000012,0x00000002,0x00040047,0x00000076, + 0x0000001e,0x00000000,0x00040047,0x0000007a,0x00000022,0x00000000,0x00040047,0x0000007a, + 0x00000021,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, + 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000002,0x00040020,0x00000008, + 0x00000007,0x00000007,0x00040017,0x0000000a,0x00000006,0x00000004,0x00040020,0x0000000b, + 0x00000001,0x0000000a,0x0004003b,0x0000000b,0x0000000c,0x00000001,0x00040015,0x0000000d, + 0x00000020,0x00000000,0x0004002b,0x0000000d,0x0000000e,0x00000000,0x00040020,0x0000000f, + 0x00000001,0x00000006,0x0007001e,0x00000012,0x00000006,0x00000006,0x00000006,0x00000006, + 0x00000006,0x00040020,0x00000013,0x00000009,0x00000012,0x0004003b,0x00000013,0x00000014, + 0x00000009,0x00040015,0x00000015,0x00000020,0x00000001,0x0004002b,0x00000015,0x00000016, + 0x00000002,0x00040020,0x00000017,0x00000009,0x00000006,0x0004002b,0x0000000d,0x0000001b, + 0x00000001,0x0004002b,0x00000015,0x0000001e,0x00000003,0x0004002b,0x00000015,0x00000023, + 0x00000000,0x0004002b,0x00000006,0x00000026,0x00000000,0x00020014,0x00000027,0x00040020, + 0x0000002b,0x00000007,0x00000006,0x0004002b,0x00000015,0x0000002d,0x00000001,0x0004002b, + 0x00000006,0x00000032,0x40000000,0x0004002b,0x00000006,0x00000052,0x40490e56,0x0004002b, + 0x00000006,0x00000054,0x41200000,0x0004002b,0x00000006,0x0000006a,0x3bda3c21,0x0004002b, + 0x00000015,0x0000006f,0x00000004,0x00040020,0x00000075,0x00000003,0x0000000a,0x0004003b, + 0x00000075,0x00000076,0x00000003,0x00090019,0x00000077,0x00000006,0x00000001,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000078,0x00000077,0x00040020, + 0x00000079,0x00000000,0x00000078,0x0004003b,0x00000079,0x0000007a,0x00000000,0x00050036, 0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000008, 0x00000009,0x00000007,0x0004003b,0x0000002b,0x0000002c,0x00000007,0x0004003b,0x0000002b, 0x0000003d,0x00000007,0x0004003b,0x0000002b,0x0000004c,0x00000007,0x0004003b,0x0000002b, @@ -87,7 +89,10 @@ const uint32_t world_warp_frag_spv[] = { 0x0000006a,0x0003003e,0x00000060,0x0000006b,0x0004003d,0x00000007,0x0000006c,0x00000060, 0x0004003d,0x00000007,0x0000006d,0x00000009,0x00050081,0x00000007,0x0000006e,0x0000006d, 0x0000006c,0x0003003e,0x00000009,0x0000006e,0x000200f9,0x0000002a,0x000200f8,0x0000002a, - 0x0004003d,0x00000072,0x00000075,0x00000074,0x0004003d,0x00000007,0x00000076,0x00000009, - 0x00050057,0x0000000a,0x00000077,0x00000075,0x00000076,0x0003003e,0x00000070,0x00000077, - 0x000100fd,0x00010038 + 0x00050041,0x00000017,0x00000070,0x00000014,0x0000006f,0x0004003d,0x00000006,0x00000071, + 0x00000070,0x0004003d,0x00000007,0x00000072,0x00000009,0x00050050,0x00000007,0x00000073, + 0x00000071,0x00000071,0x00050088,0x00000007,0x00000074,0x00000072,0x00000073,0x0003003e, + 0x00000009,0x00000074,0x0004003d,0x00000078,0x0000007b,0x0000007a,0x0004003d,0x00000007, + 0x0000007c,0x00000009,0x00050057,0x0000000a,0x0000007d,0x0000007b,0x0000007c,0x0003003e, + 0x00000076,0x0000007d,0x000100fd,0x00010038 }; \ No newline at end of file diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 95ea545e..3874772a 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -1938,6 +1938,7 @@ VkResult QVk_BeginFrame(const VkViewport* viewport, const VkRect2D* scissor) { // reset tracking variables vk_state.current_pipeline = VK_NULL_HANDLE; + vk_state.current_renderpass = RP_COUNT; vk_config.vertex_buffer_usage = 0; // triangle fan index buffer data will not be cleared between frames unless the buffer itself is too small vk_config.index_buffer_usage = vk_triangleFanIboUsage; @@ -2115,6 +2116,7 @@ void QVk_BeginRenderpass(qvkrenderpasstype_t rpType) } vkCmdBeginRenderPass(vk_commandbuffers[vk_activeBufferIdx], &renderBeginInfo[rpType], VK_SUBPASS_CONTENTS_INLINE); + vk_state.current_renderpass = rpType; } #if 0 diff --git a/src/client/refresh/vk/vk_pipeline.c b/src/client/refresh/vk/vk_pipeline.c index 2755048c..d38ca125 100644 --- a/src/client/refresh/vk/vk_pipeline.c +++ b/src/client/refresh/vk/vk_pipeline.c @@ -166,7 +166,7 @@ void QVk_CreatePipeline(const VkDescriptorSetLayout *descriptorLayout, const uin { .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .offset = 17 * sizeof(float), - .size = 4 * sizeof(float) + .size = 5 * sizeof(float) }}; VkPipelineLayoutCreateInfo plCreateInfo = { diff --git a/src/client/refresh/vk/vk_rmain.c b/src/client/refresh/vk/vk_rmain.c index ae93ae54..adccba0d 100644 --- a/src/client/refresh/vk/vk_rmain.c +++ b/src/client/refresh/vk/vk_rmain.c @@ -104,6 +104,7 @@ cvar_t *r_lockpvs; cvar_t *vk_polyblend; cvar_t *r_modulate; cvar_t *vk_shadows; +cvar_t *vk_pixel_size; cvar_t *vk_particle_size; cvar_t *vk_particle_att_a; cvar_t *vk_particle_att_b; @@ -540,7 +541,9 @@ void R_DrawParticles (void) float att_c; } particleUbo; - particleUbo.particleSize = vk_particle_size->value; + // Particle size needs to be scaled down proportionally to vk_pixel_size. + const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value); + particleUbo.particleSize = vk_particle_size->value / divisor; particleUbo.particleScale = vid.width * ri.Cvar_Get("viewsize", "100", CVAR_ARCHIVE)->value / 102400; particleUbo.minPointSize = vk_particle_min_size->value; particleUbo.maxPointSize = vk_particle_max_size->value; @@ -871,6 +874,17 @@ R_SetupVulkan (void) .minDepth = 0.f, .maxDepth = 1.f, }; + + // When rendering the world, reduce viewport size proportionally to vk_pixel_size. + if (vk_state.current_renderpass == RP_WORLD) + { + const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value); + viewport.x /= divisor; + viewport.y /= divisor; + viewport.width /= divisor; + viewport.height /= divisor; + } + vkCmdSetViewport(vk_activeCmdbuffer, 0, 1, &viewport); // set up projection matrix @@ -930,6 +944,16 @@ static void RE_RenderView (refdef_t *fd) .extent = { r_newrefdef.width, r_newrefdef.height } }; + // When rendering the world, scale down scissor proportionally to vk_pixel_size. + if (vk_state.current_renderpass == RP_WORLD) + { + const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value); + scissor.offset.x /= divisor; + scissor.offset.y /= divisor; + scissor.extent.width /= divisor; + scissor.extent.height /= divisor; + } + vkCmdSetScissor(vk_activeCmdbuffer, 0, 1, &scissor); R_PushDlights(); @@ -986,13 +1010,25 @@ qboolean RE_EndWorldRenderpass(void) // finish rendering world view to offsceen buffer vkCmdEndRenderPass(vk_activeCmdbuffer); - // apply postprocessing effects (underwater view warp if the player is submerged in liquid) to offscreen buffer + // apply postprocessing effects to offscreen buffer: + // * underwater view warp if the player is submerged in liquid + // * restore world view to the full screen size when vk_pixel_size is >1.0 QVk_BeginRenderpass(RP_WORLD_WARP); - float pushConsts[] = { (r_newrefdef.rdflags & RDF_UNDERWATER) ? r_newrefdef.time : 0.f, viewsize->value / 100, vid.width, vid.height }; + float pushConsts[] = + { + (r_newrefdef.rdflags & RDF_UNDERWATER) ? r_newrefdef.time : 0.f, + viewsize->value / 100.0f, + vid.width, + vid.height, + (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value), + }; vkCmdPushConstants(vk_activeCmdbuffer, vk_worldWarpPipeline.layout, VK_SHADER_STAGE_FRAGMENT_BIT, 17 * sizeof(float), sizeof(pushConsts), pushConsts); vkCmdBindDescriptorSets(vk_activeCmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vk_worldWarpPipeline.layout, 0, 1, &vk_colorbuffer.descriptorSet, 0, NULL); QVk_BindPipeline(&vk_worldWarpPipeline); + // Restore full viewport for future steps. + vkCmdSetViewport(vk_activeCmdbuffer, 0u, 1u, &vk_viewport); + vkCmdSetScissor(vk_activeCmdbuffer, 0u, 1u, &vk_scissor); vkCmdDraw(vk_activeCmdbuffer, 3, 1, 0, 0); vkCmdEndRenderPass(vk_activeCmdbuffer); @@ -1159,6 +1195,7 @@ R_Register( void ) vk_polyblend = ri.Cvar_Get("vk_polyblend", "1", 0); r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE); vk_shadows = ri.Cvar_Get("vk_shadows", "0", CVAR_ARCHIVE); + vk_pixel_size = ri.Cvar_Get("vk_pixel_size", "1", CVAR_ARCHIVE); vk_particle_size = ri.Cvar_Get("vk_particle_size", "40", CVAR_ARCHIVE); vk_particle_att_a = ri.Cvar_Get("vk_particle_att_a", "0.01", CVAR_ARCHIVE); vk_particle_att_b = ri.Cvar_Get("vk_particle_att_b", "0.0", CVAR_ARCHIVE); diff --git a/stuff/shaders/world_warp.frag b/stuff/shaders/world_warp.frag index 5fa6f483..3921a8b2 100644 --- a/stuff/shaders/world_warp.frag +++ b/stuff/shaders/world_warp.frag @@ -1,7 +1,8 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable -// Underwater screen warp effect similar to what software renderer provides +// Underwater screen warp effect similar to what software renderer provides. +// Pixel size to simulate lower screen resolutions is used to restore world to full screen size. layout(push_constant) uniform PushConstant { @@ -9,6 +10,7 @@ layout(push_constant) uniform PushConstant layout(offset = 72) float scale; layout(offset = 76) float scrWidth; layout(offset = 80) float scrHeight; + layout(offset = 84) float pixelSize; } pc; layout(set = 0, binding = 0) uniform sampler2D sTexture; @@ -32,5 +34,7 @@ void main() uv += distortion; } + uv /= pc.pixelSize; + fragmentColor = texture(sTexture, uv); }