mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 23:10:33 +02:00
Merge pull request #22472 from hrydgard/msaa-fixes
MSAA fixes by Claude
This commit is contained in:
18 files changed
+153
-16
No files matched your search
@@ -97,6 +97,7 @@ struct UniformDef {
|
||||
|
||||
enum class SamplerFlags {
|
||||
ARRAY_ON_VULKAN = 1,
|
||||
MULTISAMPLE = 2, // Only supported on Vulkan, read with texelFetch.
|
||||
};
|
||||
ENUM_CLASS_BITOPS(SamplerFlags);
|
||||
|
||||
|
||||
@@ -391,7 +391,9 @@ void ShaderWriter::DeclareTexture2D(const SamplerDef &def) {
|
||||
break;
|
||||
case GLSL_VULKAN:
|
||||
// texBindingBase_ is used for the thin3d descriptor set layout, where they start at 1.
|
||||
if (def.flags & SamplerFlags::ARRAY_ON_VULKAN) {
|
||||
if (def.flags & SamplerFlags::MULTISAMPLE) {
|
||||
F("layout(set = 0, binding = %d) uniform sampler2DMS%s %s;\n", def.binding + texBindingBase_, (def.flags & SamplerFlags::ARRAY_ON_VULKAN) ? "Array" : "", def.name);
|
||||
} else if (def.flags & SamplerFlags::ARRAY_ON_VULKAN) {
|
||||
F("layout(set = 0, binding = %d) uniform sampler2DArray %s;\n", def.binding + texBindingBase_, def.name);
|
||||
} else {
|
||||
F("layout(set = 0, binding = %d) uniform sampler2D %s;\n", def.binding + texBindingBase_, def.name);
|
||||
|
||||
@@ -178,7 +178,8 @@ void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VulkanBarrierBatch *barr
|
||||
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
ici.format = format;
|
||||
ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {
|
||||
// Multisampled color is sampled too, to read single samples where averaging them would be wrong.
|
||||
if (sampleCount == VK_SAMPLE_COUNT_1_BIT || color) {
|
||||
ici.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (color) {
|
||||
|
||||
@@ -989,7 +989,11 @@ void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) {
|
||||
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, const int curFrame, QueueProfileContext &profile) {
|
||||
for (size_t i = 0; i < step.preTransitions.size(); i++) {
|
||||
const TransitionRequest &iter = step.preTransitions[i];
|
||||
if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
|
||||
if (iter.samples) {
|
||||
if (iter.fb->msaaColor.image != VK_NULL_HANDLE && iter.fb->msaaColor.layout != iter.targetLayout) {
|
||||
recordBarrier_.TransitionColorImageAuto(&iter.fb->msaaColor, iter.targetLayout);
|
||||
}
|
||||
} else if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
|
||||
recordBarrier_.TransitionColorImageAuto(
|
||||
&iter.fb->color,
|
||||
iter.targetLayout
|
||||
@@ -1341,6 +1345,10 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
|
||||
}
|
||||
|
||||
recordBarrier_.TransitionColorImageAuto(&fb->color, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
if (fb->msaaColor.image != VK_NULL_HANDLE) {
|
||||
// Texturing from the samples (BindFramebufferAsTexture) moves this one out of the attachment layout.
|
||||
recordBarrier_.TransitionColorImageAuto(&fb->msaaColor, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
// If the render pass doesn't touch depth, we can avoid a layout transition of the depth buffer.
|
||||
if (fb->depth.image && RenderPassTypeHasDepth(step.render.renderPassType)) {
|
||||
|
||||
@@ -129,9 +129,10 @@ struct TransitionRequest {
|
||||
VKRFramebuffer *fb;
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VkImageLayout targetLayout;
|
||||
bool samples = false; // The multisampled color image, instead of the resolved one.
|
||||
|
||||
bool operator == (const TransitionRequest &other) const {
|
||||
return fb == other.fb && aspect == other.aspect && targetLayout == other.targetLayout;
|
||||
return fb == other.fb && aspect == other.aspect && targetLayout == other.targetLayout && samples == other.samples;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1550,7 +1550,7 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
|
||||
steps_.push_back(step);
|
||||
}
|
||||
|
||||
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int layer) {
|
||||
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int layer, bool samples) {
|
||||
_dbg_assert_(curRenderStep_ != nullptr);
|
||||
_dbg_assert_(fb != nullptr);
|
||||
|
||||
@@ -1558,6 +1558,13 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
|
||||
_dbg_assert_(aspectBit == VK_IMAGE_ASPECT_COLOR_BIT || aspectBit == VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
|
||||
// Mark the dependency, check for required transitions, and return the image.
|
||||
if (samples && fb->msaaColor.image != VK_NULL_HANDLE) {
|
||||
_dbg_assert_(aspectBit == VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
curRenderStep_->dependencies.insert(fb);
|
||||
TransitionRequest rq{ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true };
|
||||
curRenderStep_->preTransitions.insert(rq);
|
||||
return layer == -1 ? fb->msaaColor.texAllLayersView : fb->msaaColor.texLayerViews[layer];
|
||||
}
|
||||
|
||||
// Optimization: If possible, use final*Layout to put the texture into the correct layout "early".
|
||||
for (int i = (int)steps_.size() - 1; i >= 0; i--) {
|
||||
|
||||
@@ -270,7 +270,8 @@ public:
|
||||
// as the other backends, even though there's no actual binding happening here.
|
||||
// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you
|
||||
// get an array texture view.
|
||||
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);
|
||||
// With samples, binds the multisampled color image (for texelFetch of single samples) instead of the resolved one.
|
||||
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer, bool samples = false);
|
||||
|
||||
bool CopyFramebufferToMemory(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag);
|
||||
void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
|
||||
@@ -460,6 +460,7 @@ public:
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, Aspect channelBit, int layer) override;
|
||||
void BindFramebufferSamplesAsTexture(Framebuffer *fbo, int binding, int layer) override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
@@ -973,7 +974,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
|
||||
caps_.framebufferDepthBlitSupported = vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat;
|
||||
caps_.framebufferStencilBlitSupported = caps_.framebufferDepthBlitSupported;
|
||||
caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case.
|
||||
caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case.
|
||||
caps_.framebufferSeparateDepthCopySupported = true;
|
||||
caps_.framebufferSampleReadSupported = true; // Will pretty much always be the case.
|
||||
// This doesn't affect what depth/stencil format is actually used, see VulkanQueueRunner.
|
||||
caps_.preferredDepthBufferFormat = DataFormatFromVulkanDepth(vulkan->GetDeviceInfo().preferredDepthStencilFormat);
|
||||
caps_.texture3DSupported = true;
|
||||
@@ -1952,6 +1954,14 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, Aspect c
|
||||
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, layer);
|
||||
}
|
||||
|
||||
void VKContext::BindFramebufferSamplesAsTexture(Framebuffer *fbo, int binding, int layer) {
|
||||
VKFramebuffer *fb = (VKFramebuffer *)fbo;
|
||||
_assert_(binding >= 0 && binding < MAX_BOUND_TEXTURES);
|
||||
_assert_(fb != curFramebuffer_);
|
||||
boundTextures_[binding].reset(nullptr);
|
||||
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, VK_IMAGE_ASPECT_COLOR_BIT, layer, true);
|
||||
}
|
||||
|
||||
void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
||||
VKFramebuffer *fb = (VKFramebuffer *)fbo;
|
||||
if (fb) {
|
||||
|
||||
@@ -608,6 +608,7 @@ struct DeviceCaps {
|
||||
bool framebufferBlitSupported;
|
||||
bool framebufferDepthCopySupported;
|
||||
bool framebufferSeparateDepthCopySupported;
|
||||
bool framebufferSampleReadSupported = false; // BindFramebufferSamplesAsTexture works.
|
||||
bool framebufferDepthBlitSupported;
|
||||
bool framebufferStencilBlitSupported;
|
||||
bool framebufferFetchSupported;
|
||||
@@ -824,6 +825,9 @@ public:
|
||||
|
||||
// binding must be < MAX_TEXTURE_SLOTS (0, 1 are okay if it's 2).
|
||||
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, Aspect aspect, int layer) = 0;
|
||||
// Binds a multisampled framebuffer's color samples, for texelFetch from a sampler with SamplerFlags::MULTISAMPLE.
|
||||
// Check framebufferSampleReadSupported.
|
||||
virtual void BindFramebufferSamplesAsTexture(Framebuffer *fbo, int binding, int layer) {}
|
||||
|
||||
// Framebuffer fetch / input attachment support, needs to be explicit in Vulkan.
|
||||
virtual void BindCurrentFramebufferForColorInput() {}
|
||||
|
||||
@@ -233,6 +233,7 @@ static const CommandLineParam g_autoParams[] = {
|
||||
{POFF(odsLog), CmdParamType::Bool, "odslog", 'o', "Also log through OutputDebugString (Windows)", CmdLineMode::Headless},
|
||||
{POFF(generateInterpreterDispatch), CmdParamType::Bool, "generate-interpreter-dispatch", '\0', "Generate C++ interpreter dispatch code (ExecInstruction) to stdout and exit", CmdLineMode::Headless},
|
||||
{POFF(resolutionScale), CmdParamType::Int, "resolution-scale", '\0', "Set the resolution scale factor"},
|
||||
{POFF(msaa), CmdParamType::Int, "msaa", '\0', "Set the MSAA level (0 = off, 1 = 2x, 2 = 4x, 3 = 8x)"},
|
||||
{POFF(debuggerPort), CmdParamType::Int, "debugger", '\0', "Enable the WebSocket debugger on this port (0 = pick automatically); see docs/WebSocketDebugger.md"},
|
||||
{POFF(debuggerRunPort), CmdParamType::Int, "debugger-run", '\0', "Like --debugger, but starts running instead of waiting at the entry point", CmdLineMode::Headless},
|
||||
{POFF(autoSaveLoadSymbols), CmdParamType::Bool, "auto-save-load-symbols", '\0', "Auto save/load per-module and per-game symbol files (see bAutoSaveLoadSymbols)", CmdLineMode::Both},
|
||||
@@ -617,6 +618,11 @@ void CommandLineOptions::ApplyToConfig() const {
|
||||
g_Config.DoNotSaveSetting(&g_Config.iInternalResolution);
|
||||
}
|
||||
|
||||
if (msaa.has_value()) {
|
||||
g_Config.iMultiSampleLevel = msaa.value();
|
||||
g_Config.DoNotSaveSetting(&g_Config.iMultiSampleLevel);
|
||||
}
|
||||
|
||||
if (memReadAction.has_value()) {
|
||||
g_Config.iExceptionActionMemRead = memReadAction.value();
|
||||
g_Config.DoNotSaveSetting(&g_Config.iExceptionActionMemRead);
|
||||
|
||||
@@ -160,6 +160,7 @@ struct CommandLineOptions {
|
||||
|
||||
// Headless options that may also be mildly useful in application mode
|
||||
std::optional<int> resolutionScale;
|
||||
std::optional<int> msaa;
|
||||
|
||||
|
||||
// Headless options
|
||||
|
||||
@@ -69,6 +69,27 @@ Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) {
|
||||
};
|
||||
}
|
||||
|
||||
static const SamplerDef samplersMS[1] = {
|
||||
{ 0, "tex", SamplerFlags::ARRAY_ON_VULKAN | SamplerFlags::MULTISAMPLE },
|
||||
};
|
||||
|
||||
// Source and destination are the same size, so the fragment coordinate is the texel.
|
||||
Draw2DPipelineInfo GenerateDraw2DCopyColorSample0Fs(ShaderWriter &writer) {
|
||||
writer.DeclareSamplers(samplersMS);
|
||||
writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);
|
||||
writer.C(" vec4 outColor = texelFetch(tex, ivec3(ivec2(gl_FragCoord.xy), 0), 0);\n");
|
||||
writer.EndFSMain("outColor");
|
||||
|
||||
Draw2DPipelineInfo info{
|
||||
"draw2d_copy_color_sample0",
|
||||
RASTER_COLOR,
|
||||
RASTER_COLOR,
|
||||
samplersMS,
|
||||
};
|
||||
info.readSamples = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
Draw2DPipelineInfo GenerateDraw2DCopyColorRect2LinFs(ShaderWriter &writer) {
|
||||
writer.DeclareSamplers(samplers);
|
||||
writer.BeginFSMain(g_draw2Duniforms, varyings);
|
||||
@@ -374,6 +395,16 @@ Draw2DPipeline *FramebufferManagerCommon::Get2DPipeline(Draw2DShader shader) {
|
||||
}
|
||||
pipeline = draw2DPipelineColorRect2Lin_;
|
||||
break;
|
||||
|
||||
case DRAW2D_COPY_COLOR_SAMPLE0:
|
||||
if (!draw_->GetDeviceCaps().framebufferSampleReadSupported) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!draw2DPipelineColorSample0_) {
|
||||
draw2DPipelineColorSample0_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorSample0Fs);
|
||||
}
|
||||
pipeline = draw2DPipelineColorSample0_;
|
||||
break;
|
||||
case DRAW2D_COPY_DEPTH:
|
||||
if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
|
||||
// Can't do it
|
||||
|
||||
@@ -20,6 +20,7 @@ enum Draw2DShader {
|
||||
DRAW2D_565_TO_DEPTH,
|
||||
DRAW2D_565_TO_DEPTH_DESWIZZLE,
|
||||
DRAW2D_COPY_COLOR_RECT2LIN,
|
||||
DRAW2D_COPY_COLOR_SAMPLE0, // From a multisampled framebuffer, its first sample instead of the average. Needs framebufferSampleReadSupported.
|
||||
};
|
||||
|
||||
inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) {
|
||||
@@ -40,6 +41,7 @@ struct Draw2DPipelineInfo {
|
||||
RasterChannel readChannel;
|
||||
RasterChannel writeChannel;
|
||||
Slice<SamplerDef> samplers;
|
||||
bool readSamples = false; // Binds the source with BindFramebufferSamplesAsTexture.
|
||||
};
|
||||
|
||||
extern const UniformDef g_draw2Duniforms[2];
|
||||
|
||||
@@ -217,13 +217,15 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
}
|
||||
|
||||
// Note: the precision qualifiers must match the vertex shader!
|
||||
WRITE(p, "layout (location = 1) %s in lowp vec4 v_color0;\n", shading);
|
||||
// Centroid, matching the vertex shader.
|
||||
const char *colorQualifier = *shading ? shading : "centroid";
|
||||
WRITE(p, "layout (location = 1) %s in lowp vec4 v_color0;\n", colorQualifier);
|
||||
if (lmode) {
|
||||
WRITE(p, "layout (location = 2) %s in lowp vec3 v_color1;\n", shading);
|
||||
WRITE(p, "layout (location = 2) %s in lowp vec3 v_color1;\n", colorQualifier);
|
||||
}
|
||||
WRITE(p, "layout (location = 3) in highp float v_fogdepth;\n");
|
||||
if (doTexture) {
|
||||
WRITE(p, "layout (location = 0) in highp vec3 v_texcoord;\n");
|
||||
WRITE(p, "layout (location = 0) centroid in highp vec3 v_texcoord;\n");
|
||||
}
|
||||
if (fsMinmaxDiscard || fsDepthClamp) {
|
||||
WRITE(p, "layout (location = 4) in highp vec2 v_zw;\n");
|
||||
|
||||
@@ -1053,8 +1053,12 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
|
||||
void FramebufferManagerCommon::CopyDepthRect(Draw::Framebuffer *src, int srcX, int srcY, Draw::Framebuffer *dst, int dstX, int dstY, int w, int h, int scaleFactor, const char *tag) {
|
||||
const Draw::DeviceCaps &caps = draw_->GetDeviceCaps();
|
||||
const bool sameSamples = src->MultiSampleLevel() == dst->MultiSampleLevel();
|
||||
if (sameSamples && (caps.framebufferSeparateDepthCopySupported || (!caps.framebufferDepthBlitSupported && caps.framebufferCopySupported))) {
|
||||
// Only a copy takes all the samples, and blits of multisampled buffers aren't supported at all.
|
||||
const bool multisampled = src->MultiSampleLevel() != 0 || dst->MultiSampleLevel() != 0;
|
||||
if (sameSamples && (caps.framebufferSeparateDepthCopySupported || (!caps.framebufferDepthBlitSupported && caps.framebufferCopySupported) || (multisampled && caps.framebufferCopySupported))) {
|
||||
draw_->CopyFramebufferImage(src, 0, srcX, srcY, 0, dst, 0, dstX, dstY, 0, w, h, 1, Draw::Aspect::DEPTH_BIT, tag);
|
||||
} else if (multisampled) {
|
||||
WARN_LOG_ONCE(depthRectMSAA, Log::FrameBuf, "Can't copy multisampled depth here (%s)", tag);
|
||||
} else if (caps.framebufferDepthBlitSupported) {
|
||||
draw_->BlitFramebuffer(src, srcX, srcY, srcX + w, srcY + h, dst, dstX, dstY, dstX + w, dstY + h, Draw::Aspect::DEPTH_BIT, Draw::FB_BLIT_NEAREST, tag);
|
||||
} else if (caps.fragmentShaderDepthWriteSupported && caps.textureDepthSupported) {
|
||||
@@ -3076,11 +3080,33 @@ static const char *TempFBOReasonToString(TempFBO reason) {
|
||||
case TempFBO::COPY: return "copy";
|
||||
case TempFBO::STENCIL: return "stencil";
|
||||
case TempFBO::DEPTH_SHIFT: return "depth_shift";
|
||||
case TempFBO::SAMPLE0: return "sample0";
|
||||
default: break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Draw::Framebuffer *FramebufferManagerCommon::CopyColorSample0(VirtualFramebuffer *vfb) {
|
||||
if (!vfb->fbo || vfb->fbo->MultiSampleLevel() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
Draw2DPipeline *pipeline = Get2DPipeline(DRAW2D_COPY_COLOR_SAMPLE0);
|
||||
if (!pipeline) {
|
||||
return nullptr;
|
||||
}
|
||||
const int w = vfb->fbo->Width();
|
||||
const int h = vfb->fbo->Height();
|
||||
Draw::Framebuffer *copy = GetTempFBO(TempFBO::SAMPLE0, w, h);
|
||||
if (!copy) {
|
||||
return nullptr;
|
||||
}
|
||||
BlitUsingRaster(vfb->fbo, 0.0f, 0.0f, w, h, copy, 0.0f, 0.0f, w, h, false, vfb->renderScaleFactor, pipeline, "CopyColorSample0");
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
|
||||
RebindFramebuffer("After CopyColorSample0");
|
||||
return copy;
|
||||
}
|
||||
|
||||
Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h) {
|
||||
u64 key = ((u64)reason << 48) | ((u32)w << 16) | h;
|
||||
auto it = tempFBOs_.find(key);
|
||||
@@ -3090,10 +3116,12 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u
|
||||
}
|
||||
|
||||
bool z_stencil = reason == TempFBO::STENCIL || reason == TempFBO::DEPTH_SHIFT;
|
||||
// The depth shift copies to and from a framebuffer, which needs the same samples.
|
||||
const int msaaLevel = reason == TempFBO::DEPTH_SHIFT ? msaaLevel_ : 0;
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "tempfbo_%s_%dx%d", TempFBOReasonToString(reason), w / renderScaleFactor_, h / renderScaleFactor_);
|
||||
|
||||
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), 0, z_stencil, name });
|
||||
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), msaaLevel, z_stencil, name });
|
||||
if (!fbo) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3538,6 +3566,7 @@ void FramebufferManagerCommon::ReleasePipelines() {
|
||||
DoRelease(depthReadbackPipeline_);
|
||||
DoRelease(draw2DPipelineCopyColor_);
|
||||
DoRelease(draw2DPipelineColorRect2Lin_);
|
||||
DoRelease(draw2DPipelineColorSample0_);
|
||||
DoRelease(draw2DPipelineCopyDepth_);
|
||||
DoRelease(draw2DPipelineEncodeDepth_);
|
||||
DoRelease(draw2DPipeline565ToDepth_);
|
||||
@@ -3654,6 +3683,11 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
|
||||
useBlit = false;
|
||||
useCopy = false;
|
||||
}
|
||||
// Copy depth where we can: a raster copy isn't exact (Burnout Dominator's recursive rendering, #11100), and with
|
||||
// multisampling it reads the single-sampled depth, which is never resolved, while a copy takes all the samples.
|
||||
if (channel == RASTER_DEPTH && src != dst && src->fbo->MultiSampleLevel() == dst->fbo->MultiSampleLevel() && draw_->GetDeviceCaps().framebufferSeparateDepthCopySupported) {
|
||||
useCopy = true;
|
||||
}
|
||||
|
||||
float srcXFactor = src->renderScaleFactor;
|
||||
float srcYFactor = src->renderScaleFactor;
|
||||
@@ -3749,7 +3783,11 @@ void FramebufferManagerCommon::BlitUsingRaster(
|
||||
draw_->BindTexture(0, nullptr);
|
||||
// This will get optimized away in case it's already bound (in VK and GL at least..)
|
||||
draw_->BindFramebufferAsRenderTarget(dest, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag ? tag : "BlitUsingRaster");
|
||||
draw_->BindFramebufferAsTexture(src, 0, pipeline->info.readChannel == RASTER_COLOR ? Draw::Aspect::COLOR_BIT : Draw::Aspect::DEPTH_BIT, Draw::ALL_LAYERS);
|
||||
if (pipeline->info.readSamples) {
|
||||
draw_->BindFramebufferSamplesAsTexture(src, 0, Draw::ALL_LAYERS);
|
||||
} else {
|
||||
draw_->BindFramebufferAsTexture(src, 0, pipeline->info.readChannel == RASTER_COLOR ? Draw::Aspect::COLOR_BIT : Draw::Aspect::DEPTH_BIT, Draw::ALL_LAYERS);
|
||||
}
|
||||
|
||||
if (destX1 == 0.0f && destY1 == 0.0f && destX2 >= destW && destY2 >= destH) {
|
||||
// We overwrite the whole channel of the framebuffer, so we can invalidate the current contents.
|
||||
|
||||
@@ -233,6 +233,8 @@ enum class TempFBO {
|
||||
STENCIL,
|
||||
// Depth moved within one framebuffer, for rendering at an X offset.
|
||||
DEPTH_SHIFT,
|
||||
// The first samples of a multisampled framebuffer.
|
||||
SAMPLE0,
|
||||
};
|
||||
|
||||
inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) {
|
||||
@@ -460,6 +462,9 @@ public:
|
||||
virtual void DeviceRestore(Draw::DrawContext *draw);
|
||||
|
||||
Draw::Framebuffer *GetTempFBO(TempFBO reason, u16 w, u16 h);
|
||||
// A single-sampled copy of the first sample of each pixel of a multisampled framebuffer, or null if it isn't
|
||||
// multisampled or the backend can't. For reading colors as values, which averaging the samples would corrupt.
|
||||
Draw::Framebuffer *CopyColorSample0(VirtualFramebuffer *vfb);
|
||||
|
||||
// Debug features
|
||||
virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes);
|
||||
@@ -675,6 +680,7 @@ protected:
|
||||
// Draw2D pipelines
|
||||
Draw2DPipeline *draw2DPipelineCopyColor_ = nullptr;
|
||||
Draw2DPipeline *draw2DPipelineColorRect2Lin_ = nullptr;
|
||||
Draw2DPipeline *draw2DPipelineColorSample0_ = nullptr;
|
||||
Draw2DPipeline *draw2DPipelineCopyDepth_ = nullptr;
|
||||
Draw2DPipeline *draw2DPipelineEncodeDepth_ = nullptr;
|
||||
Draw2DPipeline *draw2DPipeline565ToDepth_ = nullptr;
|
||||
|
||||
@@ -2479,6 +2479,8 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
||||
bool smoothedDepal = false;
|
||||
u32 depthUpperBits = 0;
|
||||
|
||||
VirtualFramebuffer sample0Vfb;
|
||||
|
||||
if (need_depalettize) {
|
||||
if (clutRenderAddress_ == 0xFFFFFFFF) {
|
||||
clutTexture = clutTextureCache_.GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
|
||||
@@ -2495,6 +2497,17 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
||||
framebufferManager_->BlitUsingRaster(dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut");
|
||||
}
|
||||
|
||||
// With multisampling, take one sample instead of the average, since the colors are looked up as indices.
|
||||
// Dragon Ball Z: Tenkaichi Tag Team's outlines look up its framebuffer as CLUT32. (A smooth color grade
|
||||
// loses its antialiasing this way, but that's the smaller loss.)
|
||||
if (!depth) {
|
||||
if (Draw::Framebuffer *sample0 = framebufferManager_->CopyColorSample0(framebuffer)) {
|
||||
sample0Vfb = *framebuffer;
|
||||
sample0Vfb.fbo = sample0;
|
||||
framebuffer = &sample0Vfb;
|
||||
}
|
||||
}
|
||||
|
||||
if (useShaderDepal) {
|
||||
_dbg_assert_(!depth);
|
||||
|
||||
|
||||
@@ -203,12 +203,15 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
WRITE(p, "layout (location = %d) in vec3 color1;\n", (int)PspAttributeLocation::COLOR1);
|
||||
}
|
||||
|
||||
WRITE(p, "layout (location = 1) %sout lowp vec4 v_color0;\n", shading);
|
||||
// Centroid keeps them inside the primitive on pixels it only partly covers with MSAA, so that
|
||||
// textures aren't sampled from outside the UV range. Without MSAA, it's the same as center.
|
||||
const char *colorQualifier = *shading ? shading : "centroid ";
|
||||
WRITE(p, "layout (location = 1) %sout lowp vec4 v_color0;\n", colorQualifier);
|
||||
if (lmode) {
|
||||
WRITE(p, "layout (location = 2) %sout lowp vec3 v_color1;\n", shading);
|
||||
WRITE(p, "layout (location = 2) %sout lowp vec3 v_color1;\n", colorQualifier);
|
||||
}
|
||||
|
||||
WRITE(p, "layout (location = 0) out highp vec3 v_texcoord;\n");
|
||||
WRITE(p, "layout (location = 0) centroid out highp vec3 v_texcoord;\n");
|
||||
|
||||
WRITE(p, "layout (location = 3) out highp float v_fogdepth;\n");
|
||||
|
||||
|
||||
Reference in new issue
Block a user