Files
ppsspp/GPU/Common/Draw2D.h
T
Henrik RydgårdandClaude Opus 5.5 4202368b1e Vulkan: Look up multisampled framebuffers through a CLUT by their first sample
With MSAA, a framebuffer textured as CLUT indices was read resolved, so the
indices along edges were averages of the samples and looked up wrong colors.
Dragon Ball Z: Tenkaichi Tag Team draws its outlines from a CLUT32 lookup of
its framebuffer, and they came out noisy and miscolored. Copy each pixel's
first sample with texelFetch into a temporary buffer and look up that.

This needs the multisampled color images to be sampled, and transitioned
back to attachment layout before rendering to them again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00

84 lines
2.2 KiB
C++

#pragma once
#include "Data/Collections/Slice.h"
#include "GPU/GPU.h"
#include "Common/GPU/Shader.h"
#include "GPU/thin3d.h"
// For framebuffer copies and similar things that just require passthrough.
struct Draw2DVertex {
float x;
float y;
float u;
float v;
};
enum Draw2DShader {
DRAW2D_COPY_COLOR,
DRAW2D_COPY_DEPTH,
DRAW2D_ENCODE_R16_TO_DEPTH,
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) {
switch (shader) {
case DRAW2D_COPY_DEPTH:
return RASTER_DEPTH;
case DRAW2D_COPY_COLOR:
case DRAW2D_ENCODE_R16_TO_DEPTH:
case DRAW2D_565_TO_DEPTH:
case DRAW2D_565_TO_DEPTH_DESWIZZLE:
default:
return RASTER_COLOR;
}
}
struct Draw2DPipelineInfo {
const char *tag;
RasterChannel readChannel;
RasterChannel writeChannel;
Slice<SamplerDef> samplers;
bool readSamples = false; // Binds the source with BindFramebufferSamplesAsTexture.
};
extern const UniformDef g_draw2Duniforms[2];
struct Draw2DPipeline {
Draw::Pipeline *pipeline;
Draw2DPipelineInfo info;
char *code;
void Release() {
if (pipeline) {
pipeline->Release();
}
delete[] code;
delete this;
}
};
class ShaderWriter;
class Draw2D {
public:
Draw2D(Draw::DrawContext *draw) : draw_(draw) {}
void DeviceLost();
void DeviceRestore(Draw::DrawContext *draw);
Draw2DPipeline *Create2DPipeline(std::function<Draw2DPipelineInfo(ShaderWriter &)> generate);
void DrawStrip2D(Draw::Texture *tex, const Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW = 0.0f, float texH = 0.0f, int scaleFactor = 0);
void Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor);
void Ensure2DResources();
private:
Draw::DrawContext *draw_;
Draw::SamplerState *draw2DSamplerLinear_ = nullptr;
Draw::SamplerState *draw2DSamplerNearest_ = nullptr;
Draw::ShaderModule *draw2DVs_ = nullptr;
};