mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-24 00:00:13 +00:00
feat: allow FXAA alongside FSR1 and FSR3 simultaneously
- Remove !fsr_.enabled / !fsr2_.enabled guards that blocked FXAA init
- FXAA can now coexist with FSR1 and FSR3 simultaneously
- Priority: FSR3 > FXAA > FSR1
- FSR3 + FXAA: scene renders at FSR3 internal res, temporal AA runs,
then FXAA reads FSR3 history and applies spatial AA to swapchain
(replaces RCAS sharpening for ultra-quality native mode)
- FXAA + FSR1: scene renders at native res, FXAA post-processes;
FSR1 resources exist but are idle (FXAA wins for better quality)
- FSR3 only / FSR1 only: unchanged paths
- Fix missing fxaa.frag.spv: shader was present but uncompiled; the
CMake compile_shaders() function will now pick it up on next build
This commit is contained in:
parent
a97941f062
commit
eafd09aca0
1 changed files with 115 additions and 58 deletions
|
|
@ -1019,13 +1019,15 @@ void Renderer::beginFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
// FXAA resource management (disabled when FSR2 is active — FSR2 has its own AA)
|
||||
// FXAA resource management — FXAA can coexist with FSR1 and FSR3.
|
||||
// When both FXAA and FSR3 are enabled, FXAA runs as a post-FSR3 pass.
|
||||
// When both FXAA and FSR1 are enabled, FXAA takes priority (native res render).
|
||||
if (fxaa_.needsRecreate && fxaa_.sceneFramebuffer) {
|
||||
destroyFXAAResources();
|
||||
fxaa_.needsRecreate = false;
|
||||
if (!fxaa_.enabled) LOG_INFO("FXAA: disabled");
|
||||
}
|
||||
if (fxaa_.enabled && !fsr2_.enabled && !fsr_.enabled && !fxaa_.sceneFramebuffer) {
|
||||
if (fxaa_.enabled && !fxaa_.sceneFramebuffer) {
|
||||
if (!initFXAAResources()) {
|
||||
LOG_ERROR("FXAA: initialization failed, disabling");
|
||||
fxaa_.enabled = false;
|
||||
|
|
@ -1049,7 +1051,8 @@ void Renderer::beginFrame() {
|
|||
initFSR2Resources();
|
||||
}
|
||||
// Recreate FXAA resources for new swapchain dimensions
|
||||
if (fxaa_.enabled && !fsr2_.enabled && !fsr_.enabled) {
|
||||
// FXAA can coexist with FSR1 and FSR3 simultaneously.
|
||||
if (fxaa_.enabled) {
|
||||
destroyFXAAResources();
|
||||
initFXAAResources();
|
||||
}
|
||||
|
|
@ -1139,12 +1142,14 @@ void Renderer::beginFrame() {
|
|||
if (fsr2_.enabled && fsr2_.sceneFramebuffer) {
|
||||
rpInfo.framebuffer = fsr2_.sceneFramebuffer;
|
||||
renderExtent = { fsr2_.internalWidth, fsr2_.internalHeight };
|
||||
} else if (fxaa_.enabled && fxaa_.sceneFramebuffer) {
|
||||
// FXAA takes priority over FSR1: renders at native res with AA post-process.
|
||||
// When both FSR1 and FXAA are enabled, FXAA wins (native res, no downscale).
|
||||
rpInfo.framebuffer = fxaa_.sceneFramebuffer;
|
||||
renderExtent = vkCtx->getSwapchainExtent(); // native resolution — no downscaling
|
||||
} else if (fsr_.enabled && fsr_.sceneFramebuffer) {
|
||||
rpInfo.framebuffer = fsr_.sceneFramebuffer;
|
||||
renderExtent = { fsr_.internalWidth, fsr_.internalHeight };
|
||||
} else if (fxaa_.enabled && fxaa_.sceneFramebuffer) {
|
||||
rpInfo.framebuffer = fxaa_.sceneFramebuffer;
|
||||
renderExtent = vkCtx->getSwapchainExtent(); // native resolution — no downscaling
|
||||
} else {
|
||||
rpInfo.framebuffer = vkCtx->getSwapchainFramebuffers()[currentImageIndex];
|
||||
renderExtent = vkCtx->getSwapchainExtent();
|
||||
|
|
@ -1231,6 +1236,35 @@ void Renderer::endFrame() {
|
|||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
}
|
||||
|
||||
// FSR3+FXAA combined: re-point FXAA's descriptor to the FSR3 temporal output
|
||||
// so renderFXAAPass() applies spatial AA on the temporally-stabilized frame.
|
||||
// This must happen outside the render pass (descriptor updates are CPU-side).
|
||||
if (fxaa_.enabled && fxaa_.descSet && fxaa_.sceneSampler) {
|
||||
VkImageView fsr3OutputView = VK_NULL_HANDLE;
|
||||
if (fsr2_.useAmdBackend) {
|
||||
if (fsr2_.amdFsr3FramegenRuntimeActive && fsr2_.framegenOutput.image)
|
||||
fsr3OutputView = fsr2_.framegenOutput.imageView;
|
||||
else if (fsr2_.history[fsr2_.currentHistory].image)
|
||||
fsr3OutputView = fsr2_.history[fsr2_.currentHistory].imageView;
|
||||
} else if (fsr2_.history[fsr2_.currentHistory].image) {
|
||||
fsr3OutputView = fsr2_.history[fsr2_.currentHistory].imageView;
|
||||
}
|
||||
if (fsr3OutputView) {
|
||||
VkDescriptorImageInfo imgInfo{};
|
||||
imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
imgInfo.imageView = fsr3OutputView;
|
||||
imgInfo.sampler = fxaa_.sceneSampler;
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = fxaa_.descSet;
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imgInfo;
|
||||
vkUpdateDescriptorSets(vkCtx->getDevice(), 1, &write, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Begin swapchain render pass at full resolution for sharpening + ImGui
|
||||
VkRenderPassBeginInfo rpInfo{};
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
|
|
@ -1260,8 +1294,33 @@ void Renderer::endFrame() {
|
|||
sc.extent = ext;
|
||||
vkCmdSetScissor(currentCmd, 0, 1, &sc);
|
||||
|
||||
// Draw RCAS sharpening from accumulated history buffer
|
||||
renderFSR2Sharpen();
|
||||
// When FXAA is also enabled: apply FXAA on the FSR3 temporal output instead
|
||||
// of RCAS sharpening. FXAA descriptor is temporarily pointed to the FSR3
|
||||
// history buffer (which is already in SHADER_READ_ONLY_OPTIMAL). This gives
|
||||
// FSR3 temporal stability + FXAA spatial edge smoothing ("ultra quality native").
|
||||
if (fxaa_.enabled && fxaa_.pipeline && fxaa_.descSet) {
|
||||
renderFXAAPass();
|
||||
} else {
|
||||
// Draw RCAS sharpening from accumulated history buffer
|
||||
renderFSR2Sharpen();
|
||||
}
|
||||
|
||||
// Restore FXAA descriptor to its normal scene color source so standalone
|
||||
// FXAA frames are not affected by the FSR3 history pointer set above.
|
||||
if (fxaa_.enabled && fxaa_.descSet && fxaa_.sceneSampler && fxaa_.sceneColor.imageView) {
|
||||
VkDescriptorImageInfo restoreInfo{};
|
||||
restoreInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
restoreInfo.imageView = fxaa_.sceneColor.imageView;
|
||||
restoreInfo.sampler = fxaa_.sceneSampler;
|
||||
VkWriteDescriptorSet restoreWrite{};
|
||||
restoreWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
restoreWrite.dstSet = fxaa_.descSet;
|
||||
restoreWrite.dstBinding = 0;
|
||||
restoreWrite.descriptorCount = 1;
|
||||
restoreWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
restoreWrite.pImageInfo = &restoreInfo;
|
||||
vkUpdateDescriptorSets(vkCtx->getDevice(), 1, &restoreWrite, 0, nullptr);
|
||||
}
|
||||
|
||||
// Maintain frame bookkeeping
|
||||
fsr2_.prevViewProjection = camera->getViewProjectionMatrix();
|
||||
|
|
@ -1272,56 +1331,6 @@ void Renderer::endFrame() {
|
|||
}
|
||||
fsr2_.frameIndex = (fsr2_.frameIndex + 1) % 256; // Wrap to keep Halton values well-distributed
|
||||
|
||||
} else if (fsr_.enabled && fsr_.sceneFramebuffer) {
|
||||
// End the off-screen scene render pass
|
||||
vkCmdEndRenderPass(currentCmd);
|
||||
|
||||
// Transition scene color (1x resolve/color target): PRESENT_SRC_KHR → SHADER_READ_ONLY
|
||||
// The render pass finalLayout puts the resolve/color attachment in PRESENT_SRC_KHR
|
||||
transitionImageLayout(currentCmd, fsr_.sceneColor.image,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
// Begin swapchain render pass at full resolution
|
||||
VkRenderPassBeginInfo rpInfo{};
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
rpInfo.renderPass = vkCtx->getImGuiRenderPass();
|
||||
rpInfo.framebuffer = vkCtx->getSwapchainFramebuffers()[currentImageIndex];
|
||||
rpInfo.renderArea.offset = {0, 0};
|
||||
rpInfo.renderArea.extent = vkCtx->getSwapchainExtent();
|
||||
|
||||
// Clear values must match the render pass attachment count
|
||||
bool msaaOn = (vkCtx->getMsaaSamples() > VK_SAMPLE_COUNT_1_BIT);
|
||||
VkClearValue clearValues[4]{};
|
||||
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
clearValues[2].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[3].depthStencil = {1.0f, 0};
|
||||
if (msaaOn) {
|
||||
bool depthRes = (vkCtx->getDepthResolveImageView() != VK_NULL_HANDLE);
|
||||
rpInfo.clearValueCount = depthRes ? 4 : 3;
|
||||
} else {
|
||||
rpInfo.clearValueCount = 2;
|
||||
}
|
||||
rpInfo.pClearValues = clearValues;
|
||||
|
||||
vkCmdBeginRenderPass(currentCmd, &rpInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
// Set full-resolution viewport and scissor
|
||||
VkExtent2D ext = vkCtx->getSwapchainExtent();
|
||||
VkViewport vp{};
|
||||
vp.width = static_cast<float>(ext.width);
|
||||
vp.height = static_cast<float>(ext.height);
|
||||
vp.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(currentCmd, 0, 1, &vp);
|
||||
VkRect2D sc{};
|
||||
sc.extent = ext;
|
||||
vkCmdSetScissor(currentCmd, 0, 1, &sc);
|
||||
|
||||
// Draw FSR upscale fullscreen quad
|
||||
renderFSRUpscale();
|
||||
|
||||
} else if (fxaa_.enabled && fxaa_.sceneFramebuffer) {
|
||||
// End the off-screen scene render pass
|
||||
vkCmdEndRenderPass(currentCmd);
|
||||
|
|
@ -1361,9 +1370,57 @@ void Renderer::endFrame() {
|
|||
|
||||
// Draw FXAA pass
|
||||
renderFXAAPass();
|
||||
|
||||
} else if (fsr_.enabled && fsr_.sceneFramebuffer) {
|
||||
// FSR1 upscale path — only runs when FXAA is not active.
|
||||
// When both FSR1 and FXAA are enabled, FXAA took priority above.
|
||||
vkCmdEndRenderPass(currentCmd);
|
||||
|
||||
// Transition scene color (1x resolve/color target): PRESENT_SRC_KHR → SHADER_READ_ONLY
|
||||
transitionImageLayout(currentCmd, fsr_.sceneColor.image,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
// Begin swapchain render pass at full resolution
|
||||
VkRenderPassBeginInfo fsrRpInfo{};
|
||||
fsrRpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
fsrRpInfo.renderPass = vkCtx->getImGuiRenderPass();
|
||||
fsrRpInfo.framebuffer = vkCtx->getSwapchainFramebuffers()[currentImageIndex];
|
||||
fsrRpInfo.renderArea.offset = {0, 0};
|
||||
fsrRpInfo.renderArea.extent = vkCtx->getSwapchainExtent();
|
||||
|
||||
bool fsrMsaaOn = (vkCtx->getMsaaSamples() > VK_SAMPLE_COUNT_1_BIT);
|
||||
VkClearValue fsrClearValues[4]{};
|
||||
fsrClearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
fsrClearValues[1].depthStencil = {1.0f, 0};
|
||||
fsrClearValues[2].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
fsrClearValues[3].depthStencil = {1.0f, 0};
|
||||
if (fsrMsaaOn) {
|
||||
bool depthRes = (vkCtx->getDepthResolveImageView() != VK_NULL_HANDLE);
|
||||
fsrRpInfo.clearValueCount = depthRes ? 4 : 3;
|
||||
} else {
|
||||
fsrRpInfo.clearValueCount = 2;
|
||||
}
|
||||
fsrRpInfo.pClearValues = fsrClearValues;
|
||||
|
||||
vkCmdBeginRenderPass(currentCmd, &fsrRpInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkExtent2D fsrExt = vkCtx->getSwapchainExtent();
|
||||
VkViewport fsrVp{};
|
||||
fsrVp.width = static_cast<float>(fsrExt.width);
|
||||
fsrVp.height = static_cast<float>(fsrExt.height);
|
||||
fsrVp.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(currentCmd, 0, 1, &fsrVp);
|
||||
VkRect2D fsrSc{};
|
||||
fsrSc.extent = fsrExt;
|
||||
vkCmdSetScissor(currentCmd, 0, 1, &fsrSc);
|
||||
|
||||
renderFSRUpscale();
|
||||
}
|
||||
|
||||
// ImGui rendering — must respect subpass contents mode
|
||||
// Parallel recording only applies when no post-process pass is active.
|
||||
if (!fsr_.enabled && !fsr2_.enabled && !fxaa_.enabled && parallelRecordingEnabled_) {
|
||||
// Scene pass was begun with VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS,
|
||||
// so ImGui must be recorded into a secondary command buffer.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue