mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-17 04:39:34 +00:00
GLES: Add debug readback of stencil data.
This allows the existing gpu.buffer.renderStencil to snapshot the state.
This commit is contained in:
parent
c03d327ddd
commit
fc68cd9457
@ -544,8 +544,8 @@ void CheckGLExtensions() {
|
||||
}
|
||||
if (gl_extensions.VersionGEThan(4, 3)) {
|
||||
gl_extensions.ARB_copy_image = true;
|
||||
gl_extensions.ARB_stencil_texturing = true;
|
||||
// ARB_explicit_uniform_location = true;
|
||||
// ARB_stencil_texturing = true;
|
||||
// ARB_texture_view = true;
|
||||
// ARB_vertex_attrib_binding = true;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ struct GLExtensions {
|
||||
bool ARB_depth_clamp;
|
||||
bool ARB_uniform_buffer_object;
|
||||
bool ARB_texture_non_power_of_two;
|
||||
bool ARB_stencil_texturing;
|
||||
|
||||
// EXT
|
||||
bool EXT_swap_control_tear;
|
||||
|
@ -2578,6 +2578,10 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G
|
||||
// No need to free on failure, the caller/destructor will do that. Usually this is a reused buffer, anyway.
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT, flipY);
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w, "GetStencilbuffer");
|
||||
if (!retval) {
|
||||
// Try ReadbackStencilbufferSync, in case GLES.
|
||||
retval = ReadbackStencilbufferSync(vfb->fbo, 0, 0, w, h, buffer.GetData(), w);
|
||||
}
|
||||
// That may have unbound the framebuffer, rebind to avoid crashes when debugging.
|
||||
RebindFramebuffer("RebindFramebuffer - GetStencilbuffer");
|
||||
return retval;
|
||||
@ -2652,6 +2656,10 @@ bool FramebufferManagerCommon::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, i
|
||||
return draw_->CopyFramebufferToMemorySync(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, pixels, pixelsStride, "ReadbackDepthbufferSync");
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) {
|
||||
return draw_->CopyFramebufferToMemorySync(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, Draw::DataFormat::S8, pixels, pixelsStride, "ReadbackStencilbufferSync");
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
// Clamp to bufferWidth. Sometimes block transfers can cause this to hit.
|
||||
if (x + w >= vfb->bufferWidth) {
|
||||
@ -2809,6 +2817,8 @@ void FramebufferManagerCommon::DeviceLost() {
|
||||
}
|
||||
DoRelease(stencilWriteSampler_);
|
||||
DoRelease(stencilWritePipeline_);
|
||||
DoRelease(stencilReadbackSampler_);
|
||||
DoRelease(stencilReadbackPipeline_);
|
||||
DoRelease(depthReadbackSampler_);
|
||||
DoRelease(depthReadbackPipeline_);
|
||||
DoRelease(draw2DPipelineColor_);
|
||||
|
@ -444,6 +444,7 @@ protected:
|
||||
virtual void ReadbackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
// Used for when a shader is required, such as GLES.
|
||||
virtual bool ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride);
|
||||
virtual bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride);
|
||||
void SetViewport2D(int x, int y, int w, int h);
|
||||
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags);
|
||||
@ -571,6 +572,9 @@ protected:
|
||||
Draw::Pipeline *stencilWritePipeline_ = nullptr;
|
||||
Draw::SamplerState *stencilWriteSampler_ = nullptr;
|
||||
|
||||
// Used on GLES where we can't directly readback depth or stencil, but here for simplicity.
|
||||
Draw::Pipeline *stencilReadbackPipeline_ = nullptr;
|
||||
Draw::SamplerState *stencilReadbackSampler_ = nullptr;
|
||||
Draw::Pipeline *depthReadbackPipeline_ = nullptr;
|
||||
Draw::SamplerState *depthReadbackSampler_ = nullptr;
|
||||
|
||||
|
@ -85,6 +85,45 @@ const UniformBufferDesc depthUBDesc{ sizeof(DepthUB), {
|
||||
{ "u_depthTo8", -1, -1, UniformType::FLOAT4, 32 },
|
||||
} };
|
||||
|
||||
static const char *stencil_dl_fs = R"(
|
||||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
#define texture2D texture
|
||||
#define gl_FragColor fragColor0
|
||||
out vec4 fragColor0;
|
||||
#endif
|
||||
varying vec2 v_texcoord;
|
||||
lowp uniform usampler2D tex;
|
||||
void main() {
|
||||
uint stencil = texture2D(tex, v_texcoord).r;
|
||||
float scaled = float(stencil) / 255.0;
|
||||
gl_FragColor = vec4(scaled, scaled, scaled, scaled);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char *stencil_vs = R"(
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
#if __VERSION__ >= 130
|
||||
#define attribute in
|
||||
#define varying out
|
||||
#endif
|
||||
attribute vec2 a_position;
|
||||
varying vec2 v_texcoord;
|
||||
void main() {
|
||||
v_texcoord = a_position * 2.0;
|
||||
gl_Position = vec4(v_texcoord * 2.0 - vec2(1.0, 1.0), 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
static bool SupportsDepthTexturing() {
|
||||
if (gl_extensions.IsGLES) {
|
||||
return gl_extensions.OES_packed_depth_stencil && (gl_extensions.OES_depth_texture || gl_extensions.GLES3);
|
||||
@ -248,3 +287,75 @@ bool FramebufferManagerGLES::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int
|
||||
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Well, this is not depth, but it's depth/stencil related.
|
||||
bool FramebufferManagerGLES::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) {
|
||||
using namespace Draw;
|
||||
|
||||
if (!fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "ReadbackStencilbufferSync: bad fbo");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool useColorPath = gl_extensions.IsGLES;
|
||||
if (!useColorPath) {
|
||||
return draw_->CopyFramebufferToMemorySync(fbo, FB_STENCIL_BIT, x, y, w, h, DataFormat::S8, pixels, pixelsStride, "ReadbackStencilbufferSync");
|
||||
}
|
||||
|
||||
// Unsupported below GLES 3.1 or without ARB_stencil_texturing.
|
||||
// OES_texture_stencil8 is related, but used to specify texture data.
|
||||
if ((gl_extensions.IsGLES && !gl_extensions.VersionGEThan(3, 1)) && !gl_extensions.ARB_stencil_texturing)
|
||||
return false;
|
||||
|
||||
// Pixel size always 4 here because we always request RGBA back.
|
||||
const u32 bufSize = w * h * 4;
|
||||
if (!convBuf_ || convBufSize_ < bufSize) {
|
||||
delete[] convBuf_;
|
||||
convBuf_ = new u8[bufSize];
|
||||
convBufSize_ = bufSize;
|
||||
}
|
||||
|
||||
if (!stencilReadbackPipeline_) {
|
||||
stencilReadbackPipeline_ = CreateReadbackPipeline(draw_, "stencil_dl", &depthUBDesc, stencil_dl_fs, "stencil_dl_fs", stencil_vs, "stencil_vs");
|
||||
stencilReadbackSampler_ = draw_->CreateSamplerState({});
|
||||
}
|
||||
|
||||
shaderManager_->DirtyLastShader();
|
||||
auto *blitFBO = GetTempFBO(TempFBO::COPY, fbo->Width(), fbo->Height());
|
||||
draw_->BindFramebufferAsRenderTarget(blitFBO, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackStencilbufferSync");
|
||||
Draw::Viewport viewport = { 0.0f, 0.0f, (float)fbo->Width(), (float)fbo->Height(), 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &viewport);
|
||||
|
||||
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_STENCIL_BIT, 0);
|
||||
draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &stencilReadbackSampler_);
|
||||
|
||||
// We must bind the program after starting the render pass.
|
||||
draw_->SetScissorRect(0, 0, w, h);
|
||||
draw_->BindPipeline(stencilReadbackPipeline_);
|
||||
|
||||
// Fullscreen triangle coordinates.
|
||||
static const float positions[6] = {
|
||||
0.0, 0.0,
|
||||
1.0, 0.0,
|
||||
0.0, 1.0,
|
||||
};
|
||||
draw_->DrawUP(positions, 3);
|
||||
|
||||
draw_->CopyFramebufferToMemorySync(blitFBO, FB_COLOR_BIT, x, y, w, h, DataFormat::R8G8B8A8_UNORM, convBuf_, w, "ReadbackStencilbufferSync");
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
|
||||
// TODO: Use 1/4 width to write all values directly and skip CPU conversion?
|
||||
uint8_t *dest = pixels;
|
||||
const u32_le *packed32 = (u32_le *)convBuf_;
|
||||
for (int yp = 0; yp < h; ++yp) {
|
||||
for (int xp = 0; xp < w; ++xp) {
|
||||
dest[xp] = packed32[xp] & 0xFF;
|
||||
}
|
||||
dest += pixelsStride;
|
||||
packed32 += w;
|
||||
}
|
||||
|
||||
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
|
||||
return true;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
protected:
|
||||
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
|
||||
bool ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride) override;
|
||||
bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) override;
|
||||
|
||||
private:
|
||||
u8 *convBuf_ = nullptr;
|
||||
|
Loading…
x
Reference in New Issue
Block a user