mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #16914 from hrydgard/readback-more-prep
Some more plumbing of parameters, preparing for readback stuff
This commit is contained in:
18 files changed
+202
-205
No files matched your search
@@ -1615,7 +1615,7 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
|
||||
void GLQueueRunner::CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
|
||||
// TODO: Maybe move data format conversion here, and always read back 8888. Drivers
|
||||
// don't usually provide very optimized conversion implementations, though some do.
|
||||
// Just need to be careful about dithering, which may break Danganronpa.
|
||||
|
||||
@@ -373,7 +373,7 @@ public:
|
||||
return (int)depth * 3 + (int)color;
|
||||
}
|
||||
|
||||
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
void CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
|
||||
void Resize(int width, int height) {
|
||||
targetWidth_ = width;
|
||||
|
||||
@@ -360,7 +360,7 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
|
||||
steps_.push_back(step);
|
||||
}
|
||||
|
||||
bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
|
||||
bool GLRenderManager::CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag) {
|
||||
_assert_(pixels);
|
||||
|
||||
GLRStep *step = new GLRStep{ GLRStepType::READBACK };
|
||||
@@ -387,7 +387,7 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
queueRunner_.CopyFromReadbackBuffer(src, w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, i
|
||||
curRenderStep_ = nullptr;
|
||||
FlushSync();
|
||||
|
||||
queueRunner_.CopyReadbackBuffer(w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels);
|
||||
queueRunner_.CopyFromReadbackBuffer(nullptr, w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels);
|
||||
}
|
||||
|
||||
void GLRenderManager::BeginFrame() {
|
||||
|
||||
@@ -570,7 +570,7 @@ public:
|
||||
// Binds a framebuffer as a texture, for the following draws.
|
||||
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit);
|
||||
|
||||
bool CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
bool CopyFramebufferToMemory(GLRFramebuffer *src, int 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(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
|
||||
void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask, const char *tag);
|
||||
|
||||
@@ -1001,7 +1001,7 @@ bool OpenGLContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, i
|
||||
aspect |= GL_DEPTH_BUFFER_BIT;
|
||||
if (channelBits & FB_STENCIL_BIT)
|
||||
aspect |= GL_STENCIL_BUFFER_BIT;
|
||||
renderManager_.CopyFramebufferToMemorySync(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, tag);
|
||||
renderManager_.CopyFramebufferToMemory(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, mode, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2074,7 +2074,7 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe
|
||||
// Doing that will also act like a heavyweight barrier ensuring that device writes are visible on the host.
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
|
||||
void VulkanQueueRunner::CopyReadbackBuffer(VKRFramebuffer *src, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
|
||||
if (!readbackBuffer_)
|
||||
return; // Something has gone really wrong.
|
||||
|
||||
@@ -2082,15 +2082,16 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm
|
||||
void *mappedData;
|
||||
const size_t srcPixelSize = DataFormatSizeInBytes(srcFormat);
|
||||
VkResult res = vmaMapMemory(vulkan_->Allocator(), readbackAllocation_, &mappedData);
|
||||
if (!readbackBufferIsCoherent_) {
|
||||
vmaInvalidateAllocation(vulkan_->Allocator(), readbackAllocation_, 0, width * height * srcPixelSize);
|
||||
}
|
||||
|
||||
if (res != VK_SUCCESS) {
|
||||
ERROR_LOG(G3D, "CopyReadbackBuffer: vkMapMemory failed! result=%d", (int)res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!readbackBufferIsCoherent_) {
|
||||
vmaInvalidateAllocation(vulkan_->Allocator(), readbackAllocation_, 0, width * height * srcPixelSize);
|
||||
}
|
||||
|
||||
// TODO: Perform these conversions in a compute shader on the GPU.
|
||||
if (srcFormat == Draw::DataFormat::R8G8B8A8_UNORM) {
|
||||
ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat);
|
||||
|
||||
@@ -252,7 +252,8 @@ public:
|
||||
return (int)depth * 3 + (int)color;
|
||||
}
|
||||
|
||||
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
// src == 0 means to copy from the sync readback buffer.
|
||||
void CopyReadbackBuffer(VKRFramebuffer *src, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
|
||||
VKRRenderPass *GetRenderPass(const RPKey &key);
|
||||
|
||||
|
||||
@@ -912,8 +912,9 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
|
||||
}
|
||||
}
|
||||
|
||||
bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
|
||||
bool VulkanRenderManager::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) {
|
||||
_dbg_assert_(insideFrame_);
|
||||
|
||||
for (int i = (int)steps_.size() - 1; i >= 0; i--) {
|
||||
if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == src) {
|
||||
steps_[i]->render.numReads++;
|
||||
@@ -971,7 +972,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
|
||||
}
|
||||
|
||||
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
|
||||
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
queueRunner_.CopyReadbackBuffer(src, w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -991,7 +992,7 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int
|
||||
FlushSync();
|
||||
|
||||
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
|
||||
queueRunner_.CopyReadbackBuffer(w, h, destFormat, destFormat, pixelStride, pixels);
|
||||
queueRunner_.CopyReadbackBuffer(nullptr, w, h, destFormat, destFormat, pixelStride, pixels);
|
||||
}
|
||||
|
||||
static void RemoveDrawCommands(std::vector<VkRenderData> *cmds) {
|
||||
|
||||
@@ -217,7 +217,7 @@ public:
|
||||
|
||||
void BindCurrentFramebufferAsInputAttachment0(VkImageAspectFlags aspectBits);
|
||||
|
||||
bool CopyFramebufferToMemorySync(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
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);
|
||||
|
||||
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);
|
||||
|
||||
@@ -1640,7 +1640,7 @@ bool VKContext::CopyFramebufferToMemory(Framebuffer *srcfb, int channelBits, int
|
||||
if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
|
||||
return renderManager_.CopyFramebufferToMemorySync(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride, tag);
|
||||
return renderManager_.CopyFramebufferToMemory(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride, mode, tag);
|
||||
}
|
||||
|
||||
DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) {
|
||||
|
||||
@@ -164,7 +164,7 @@ Draw::Pipeline *CreateReadbackPipeline(Draw::DrawContext *draw, const char *tag,
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH) {
|
||||
bool FramebufferManagerCommon::ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH, Draw::ReadbackMode mode) {
|
||||
using namespace Draw;
|
||||
|
||||
if (!fbo) {
|
||||
@@ -244,13 +244,13 @@ bool FramebufferManagerCommon::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, i
|
||||
|
||||
draw_->CopyFramebufferToMemory(blitFBO, FB_COLOR_BIT,
|
||||
x * scaleX, y * scaleY, w * scaleX, h * scaleY,
|
||||
DataFormat::R8G8B8A8_UNORM, convBuf_, destW, ReadbackMode::BLOCK, "ReadbackDepthbufferSync");
|
||||
DataFormat::R8G8B8A8_UNORM, convBuf_, destW, mode, "ReadbackDepthbufferSync");
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
// TODO: Use 4444 (or better, R16_UNORM) so we can copy lines directly (instead of 32 -> 16 on CPU)?
|
||||
format16Bit = true;
|
||||
} else {
|
||||
draw_->CopyFramebufferToMemory(fbo, FB_DEPTH_BIT, x, y, w, h, DataFormat::D32F, convBuf_, w, ReadbackMode::BLOCK, "ReadbackDepthbufferSync");
|
||||
draw_->CopyFramebufferToMemory(fbo, FB_DEPTH_BIT, x, y, w, h, DataFormat::D32F, convBuf_, w, mode, "ReadbackDepthbufferSync");
|
||||
format16Bit = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1018,7 +1018,7 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v
|
||||
|
||||
// TODO: This type of download could be made async, for less stutter on framebuffer creation.
|
||||
if (!g_Config.bSkipGPUReadbacks && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR, Draw::ReadbackMode::BLOCK);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
vfb->safeWidth = 0;
|
||||
vfb->safeHeight = 0;
|
||||
@@ -1042,14 +1042,15 @@ bool FramebufferManagerCommon::ShouldDownloadFramebufferDepth(const VirtualFrame
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) {
|
||||
// TODO: Isn't this wrong? Shouldn't we download the prevVfb if anything?
|
||||
if (ShouldDownloadFramebufferColor(vfb) && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR, Draw::ReadbackMode::BLOCK);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
} else {
|
||||
DownloadFramebufferOnSwitch(prevVfb);
|
||||
}
|
||||
|
||||
if (prevVfb && ShouldDownloadFramebufferDepth(prevVfb)) {
|
||||
ReadFramebufferToMemory(prevVfb, 0, 0, prevVfb->width, prevVfb->height, RasterChannel::RASTER_DEPTH);
|
||||
// Allow old data here to avoid blocking, if possible - no uses cases for this depend on data being super fresh.
|
||||
ReadFramebufferToMemory(prevVfb, 0, 0, prevVfb->width, prevVfb->height, RasterChannel::RASTER_DEPTH, Draw::ReadbackMode::OLD_DATA_OK);
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
@@ -1586,7 +1587,7 @@ void FramebufferManagerCommon::DecimateFBOs() {
|
||||
int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used);
|
||||
|
||||
if (ShouldDownloadFramebufferColor(vfb) && age == 0 && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR, Draw::ReadbackMode::BLOCK);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
|
||||
@@ -1895,7 +1896,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) {
|
||||
WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight);
|
||||
} else if (!g_Config.bSkipGPUReadbacks && (!srcBuffer->memoryUpdated || channel == RASTER_DEPTH)) {
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, channel);
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, channel, Draw::ReadbackMode::BLOCK);
|
||||
srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
return false;
|
||||
@@ -2365,7 +2366,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
||||
if (tooTall) {
|
||||
WARN_LOG_ONCE(btdheight, G3D, "Block transfer download %08x -> %08x dangerous, %d+%d is taller than %d", srcBasePtr, dstBasePtr, srcRect.y, srcRect.h, srcRect.vfb->bufferHeight);
|
||||
}
|
||||
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR);
|
||||
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR, Draw::ReadbackMode::BLOCK);
|
||||
srcRect.vfb->usageFlags = (srcRect.vfb->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
}
|
||||
@@ -2662,25 +2663,18 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32
|
||||
|
||||
bool flipY = (GetGPUBackend() == GPUBackend::OPENGL && !useBufferedRendering_) ? true : false;
|
||||
|
||||
bool retval;
|
||||
if (true) {
|
||||
// Always use ReadbackDepthbufferSync (while we debug it)
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_16BIT, flipY);
|
||||
retval = ReadbackDepthbufferSync(vfb->fbo, 0, 0, w, h, (uint16_t *)buffer.GetData(), w, w, h);
|
||||
// Old code
|
||||
if (gstate_c.Use(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256, flipY);
|
||||
} else {
|
||||
// Old code
|
||||
if (gstate_c.Use(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256, flipY);
|
||||
} else {
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT, flipY);
|
||||
}
|
||||
// No need to free on failure, that's the caller's job (it likely will reuse a buffer.)
|
||||
retval = draw_->CopyFramebufferToMemory(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w, Draw::ReadbackMode::BLOCK, "GetDepthBuffer");
|
||||
if (!retval) {
|
||||
// Try ReadbackDepthbufferSync, in case GLES.
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_16BIT, flipY);
|
||||
retval = ReadbackDepthbufferSync(vfb->fbo, 0, 0, w, h, (uint16_t *)buffer.GetData(), w, w, h);
|
||||
}
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT, flipY);
|
||||
}
|
||||
// No need to free on failure, that's the caller's job (it likely will reuse a buffer.)
|
||||
bool retval = draw_->CopyFramebufferToMemory(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w, Draw::ReadbackMode::BLOCK, "GetDepthBuffer");
|
||||
if (!retval) {
|
||||
// Try ReadbackDepthbufferSync, in case GLES.
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_16BIT, flipY);
|
||||
retval = ReadbackDepthbuffer(vfb->fbo, 0, 0, w, h, (uint16_t *)buffer.GetData(), w, w, h, Draw::ReadbackMode::BLOCK);
|
||||
}
|
||||
|
||||
// After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe.
|
||||
@@ -2718,8 +2712,7 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G
|
||||
buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT, flipY);
|
||||
bool retval = draw_->CopyFramebufferToMemory(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w, Draw::ReadbackMode::BLOCK, "GetStencilbuffer");
|
||||
if (!retval) {
|
||||
// Try ReadbackStencilbufferSync, in case GLES.
|
||||
retval = ReadbackStencilbufferSync(vfb->fbo, 0, 0, w, h, buffer.GetData(), w);
|
||||
retval = ReadbackStencilbuffer(vfb->fbo, 0, 0, w, h, buffer.GetData(), w, Draw::ReadbackMode::BLOCK);
|
||||
}
|
||||
// That may have unbound the framebuffer, rebind to avoid crashes when debugging.
|
||||
RebindFramebuffer("RebindFramebuffer - GetStencilbuffer");
|
||||
@@ -2746,7 +2739,7 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
|
||||
// (Except using the GPU might cause problems because of various implementations'
|
||||
// dithering behavior and games that expect exact colors like Danganronpa, so we
|
||||
// can't entirely be rid of the CPU path.) -- unknown
|
||||
void FramebufferManagerCommon::ReadbackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
void FramebufferManagerCommon::ReadbackFramebuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode) {
|
||||
if (w <= 0 || h <= 0) {
|
||||
ERROR_LOG(G3D, "Bad inputs to ReadbackFramebufferSync: %d %d %d %d", x, y, w, h);
|
||||
return;
|
||||
@@ -2788,11 +2781,11 @@ void FramebufferManagerCommon::ReadbackFramebufferSync(VirtualFramebuffer *vfb,
|
||||
|
||||
if (channel == RASTER_DEPTH) {
|
||||
_assert_msg_(vfb && vfb->z_address != 0 && vfb->z_stride != 0, "Depth buffer invalid");
|
||||
ReadbackDepthbufferSync(vfb->fbo,
|
||||
ReadbackDepthbuffer(vfb->fbo,
|
||||
x * vfb->renderScaleFactor, y * vfb->renderScaleFactor,
|
||||
w * vfb->renderScaleFactor, h * vfb->renderScaleFactor, (uint16_t *)destPtr, stride, w, h);
|
||||
w * vfb->renderScaleFactor, h * vfb->renderScaleFactor, (uint16_t *)destPtr, stride, w, h, mode);
|
||||
} else {
|
||||
draw_->CopyFramebufferToMemory(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, stride, Draw::ReadbackMode::BLOCK, "ReadbackFramebufferSync");
|
||||
draw_->CopyFramebufferToMemory(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, stride, mode, "ReadbackFramebufferSync");
|
||||
}
|
||||
|
||||
char tag[128];
|
||||
@@ -2802,11 +2795,11 @@ void FramebufferManagerCommon::ReadbackFramebufferSync(VirtualFramebuffer *vfb,
|
||||
gpuStats.numReadbacks++;
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) {
|
||||
return draw_->CopyFramebufferToMemory(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, Draw::DataFormat::S8, pixels, pixelsStride, Draw::ReadbackMode::BLOCK, "ReadbackStencilbufferSync");
|
||||
bool FramebufferManagerCommon::ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride, Draw::ReadbackMode mode) {
|
||||
return draw_->CopyFramebufferToMemory(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, Draw::DataFormat::S8, pixels, pixelsStride, mode, "ReadbackStencilbufferSync");
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode) {
|
||||
// Clamp to bufferWidth. Sometimes block transfers can cause this to hit.
|
||||
if (x + w >= vfb->bufferWidth) {
|
||||
w = vfb->bufferWidth - x;
|
||||
@@ -2843,7 +2836,7 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb,
|
||||
}
|
||||
|
||||
// This handles any required stretching internally.
|
||||
ReadbackFramebufferSync(vfb, x, y, w, h, channel);
|
||||
ReadbackFramebuffer(vfb, x, y, w, h, channel, mode);
|
||||
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
textureCache_->ForgetLastTexture();
|
||||
@@ -2895,7 +2888,7 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo
|
||||
vfb->clutUpdatedBytes = loadBytes;
|
||||
|
||||
// This function now handles scaling down internally.
|
||||
ReadbackFramebufferSync(vfb, x, y, w, h, RASTER_COLOR);
|
||||
ReadbackFramebuffer(vfb, x, y, w, h, RASTER_COLOR, Draw::ReadbackMode::BLOCK);
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
RebindFramebuffer("RebindFramebuffer - DownloadFramebufferForClut");
|
||||
|
||||
@@ -328,7 +328,7 @@ public:
|
||||
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags, int layer);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);
|
||||
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);
|
||||
@@ -457,10 +457,10 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void ReadbackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
virtual void ReadbackFramebuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);
|
||||
// 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, int destW, int destH);
|
||||
virtual bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride);
|
||||
virtual bool ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH, Draw::ReadbackMode mode);
|
||||
virtual bool ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride, Draw::ReadbackMode mode);
|
||||
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);
|
||||
|
||||
+142
-137
@@ -1238,158 +1238,163 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) {
|
||||
clutTotalBytes_ = loadBytes;
|
||||
clutRenderAddress_ = 0xFFFFFFFF;
|
||||
|
||||
if (Memory::IsValidAddress(clutAddr)) {
|
||||
if (Memory::IsVRAMAddress(clutAddr)) {
|
||||
// Clear the uncached and mirror bits, etc. to match framebuffers.
|
||||
const u32 clutLoadAddr = clutAddr & 0x041FFFFF;
|
||||
const u32 clutLoadEnd = clutLoadAddr + loadBytes;
|
||||
static const u32 MAX_CLUT_OFFSET = 4096;
|
||||
if (!Memory::IsValidAddress(clutAddr)) {
|
||||
memset(clutBufRaw_, 0x00, loadBytes);
|
||||
// Reload the clut next time (should we really do it in this case?)
|
||||
clutLastFormat_ = 0xFFFFFFFF;
|
||||
clutMaxBytes_ = std::max(clutMaxBytes_, loadBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
clutRenderOffset_ = MAX_CLUT_OFFSET;
|
||||
const std::vector<VirtualFramebuffer *> &framebuffers = framebufferManager_->Framebuffers();
|
||||
if (Memory::IsVRAMAddress(clutAddr)) {
|
||||
// Clear the uncached and mirror bits, etc. to match framebuffers.
|
||||
const u32 clutLoadAddr = clutAddr & 0x041FFFFF;
|
||||
const u32 clutLoadEnd = clutLoadAddr + loadBytes;
|
||||
static const u32 MAX_CLUT_OFFSET = 4096;
|
||||
|
||||
u32 bestClutAddress = 0xFFFFFFFF;
|
||||
clutRenderOffset_ = MAX_CLUT_OFFSET;
|
||||
const std::vector<VirtualFramebuffer *> &framebuffers = framebufferManager_->Framebuffers();
|
||||
|
||||
VirtualFramebuffer *chosenFramebuffer = nullptr;
|
||||
for (VirtualFramebuffer *framebuffer : framebuffers) {
|
||||
// Let's not deal with divide by zero.
|
||||
if (framebuffer->fb_stride == 0)
|
||||
continue;
|
||||
u32 bestClutAddress = 0xFFFFFFFF;
|
||||
|
||||
const u32 fb_address = framebuffer->fb_address;
|
||||
const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format);
|
||||
int offset = clutLoadAddr - fb_address;
|
||||
VirtualFramebuffer *chosenFramebuffer = nullptr;
|
||||
for (VirtualFramebuffer *framebuffer : framebuffers) {
|
||||
// Let's not deal with divide by zero.
|
||||
if (framebuffer->fb_stride == 0)
|
||||
continue;
|
||||
|
||||
// Is this inside the framebuffer at all? Note that we only check the first line here, this should
|
||||
// be changed.
|
||||
bool matchRange = offset >= 0 && offset < (int)(framebuffer->fb_stride * fb_bpp);
|
||||
if (matchRange) {
|
||||
// And is it inside the rendered area? Sometimes games pack data in the margin between width and stride.
|
||||
// If the framebuffer width was detected as 512, we're gonna assume it's really 480.
|
||||
int fbMatchWidth = framebuffer->width;
|
||||
if (fbMatchWidth == 512) {
|
||||
fbMatchWidth = 480;
|
||||
}
|
||||
bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth;
|
||||
const u32 fb_address = framebuffer->fb_address;
|
||||
const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format);
|
||||
int offset = clutLoadAddr - fb_address;
|
||||
|
||||
// The offset check here means, in the context of the loop, that we'll pick
|
||||
// the framebuffer with the smallest offset. This is yet another framebuffer matching
|
||||
// loop with its own rules, eventually we'll probably want to do something
|
||||
// more systematic.
|
||||
if (matchRange && !inMargin && offset < (int)clutRenderOffset_) {
|
||||
WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset);
|
||||
framebuffer->last_frame_clut = gpuStats.numFlips;
|
||||
// Also mark used so it's not decimated.
|
||||
framebuffer->last_frame_used = gpuStats.numFlips;
|
||||
framebuffer->usageFlags |= FB_USAGE_CLUT;
|
||||
bestClutAddress = framebuffer->fb_address;
|
||||
clutRenderOffset_ = (u32)offset;
|
||||
chosenFramebuffer = framebuffer;
|
||||
if (offset == 0) {
|
||||
// Not gonna find a better match according to the smallest-offset rule, so we'll go with this one.
|
||||
break;
|
||||
}
|
||||
// Is this inside the framebuffer at all? Note that we only check the first line here, this should
|
||||
// be changed.
|
||||
bool matchRange = offset >= 0 && offset < (int)(framebuffer->fb_stride * fb_bpp);
|
||||
if (matchRange) {
|
||||
// And is it inside the rendered area? Sometimes games pack data in the margin between width and stride.
|
||||
// If the framebuffer width was detected as 512, we're gonna assume it's really 480.
|
||||
int fbMatchWidth = framebuffer->width;
|
||||
if (fbMatchWidth == 512) {
|
||||
fbMatchWidth = 480;
|
||||
}
|
||||
bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth;
|
||||
|
||||
// The offset check here means, in the context of the loop, that we'll pick
|
||||
// the framebuffer with the smallest offset. This is yet another framebuffer matching
|
||||
// loop with its own rules, eventually we'll probably want to do something
|
||||
// more systematic.
|
||||
if (matchRange && !inMargin && offset < (int)clutRenderOffset_) {
|
||||
WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset);
|
||||
framebuffer->last_frame_clut = gpuStats.numFlips;
|
||||
// Also mark used so it's not decimated.
|
||||
framebuffer->last_frame_used = gpuStats.numFlips;
|
||||
framebuffer->usageFlags |= FB_USAGE_CLUT;
|
||||
bestClutAddress = framebuffer->fb_address;
|
||||
clutRenderOffset_ = (u32)offset;
|
||||
chosenFramebuffer = framebuffer;
|
||||
if (offset == 0) {
|
||||
// Not gonna find a better match according to the smallest-offset rule, so we'll go with this one.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To turn off dynamic CLUT (for demonstration or testing purposes), add "false &&" to this check.
|
||||
if (chosenFramebuffer && chosenFramebuffer->fbo) {
|
||||
clutRenderAddress_ = bestClutAddress;
|
||||
|
||||
if (!dynamicClutTemp_) {
|
||||
Draw::FramebufferDesc desc{};
|
||||
desc.width = 512;
|
||||
desc.height = 1;
|
||||
desc.depth = 1;
|
||||
desc.z_stencil = false;
|
||||
desc.numLayers = 1;
|
||||
desc.multiSampleLevel = 0;
|
||||
desc.tag = "dynamic_clut";
|
||||
dynamicClutFbo_ = draw_->CreateFramebuffer(desc);
|
||||
desc.tag = "dynamic_clut_temp";
|
||||
dynamicClutTemp_ = draw_->CreateFramebuffer(desc);
|
||||
}
|
||||
|
||||
// We'll need to copy from the offset.
|
||||
const u32 fb_bpp = BufferFormatBytesPerPixel(chosenFramebuffer->fb_format);
|
||||
const int totalPixelsOffset = clutRenderOffset_ / fb_bpp;
|
||||
const int clutYOffset = totalPixelsOffset / chosenFramebuffer->fb_stride;
|
||||
const int clutXOffset = totalPixelsOffset % chosenFramebuffer->fb_stride;
|
||||
const int scale = chosenFramebuffer->renderScaleFactor;
|
||||
|
||||
// Copy the pixels to our temp clut, scaling down if needed and wrapping.
|
||||
framebufferManager_->BlitUsingRaster(
|
||||
chosenFramebuffer->fbo, clutXOffset * scale, clutYOffset * scale, (clutXOffset + 512.0f) * scale, (clutYOffset + 1.0f) * scale,
|
||||
dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f,
|
||||
false, scale, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR_RECT2LIN), "copy_clut_to_temp");
|
||||
|
||||
framebufferManager_->RebindFramebuffer("after_copy_clut_to_temp");
|
||||
clutRenderFormat_ = chosenFramebuffer->fb_format;
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::ALLOC, clutAddr, loadBytes, "CLUT");
|
||||
}
|
||||
|
||||
// It's possible for a game to load CLUT outside valid memory without crashing, should result in zeroes.
|
||||
u32 bytes = Memory::ValidSize(clutAddr, loadBytes);
|
||||
_assert_(bytes <= 2048);
|
||||
bool performDownload = PSP_CoreParameter().compat.flags().AllowDownloadCLUT;
|
||||
if (GPURecord::IsActive())
|
||||
performDownload = true;
|
||||
if (clutRenderAddress_ != 0xFFFFFFFF && performDownload) {
|
||||
framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes);
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
// To turn off dynamic CLUT (for demonstration or testing purposes), add "false &&" to this check.
|
||||
if (chosenFramebuffer && chosenFramebuffer->fbo) {
|
||||
clutRenderAddress_ = bestClutAddress;
|
||||
|
||||
if (!dynamicClutTemp_) {
|
||||
Draw::FramebufferDesc desc{};
|
||||
desc.width = 512;
|
||||
desc.height = 1;
|
||||
desc.depth = 1;
|
||||
desc.z_stencil = false;
|
||||
desc.numLayers = 1;
|
||||
desc.multiSampleLevel = 0;
|
||||
desc.tag = "dynamic_clut";
|
||||
dynamicClutFbo_ = draw_->CreateFramebuffer(desc);
|
||||
desc.tag = "dynamic_clut_temp";
|
||||
dynamicClutTemp_ = draw_->CreateFramebuffer(desc);
|
||||
}
|
||||
} else {
|
||||
// Here we could check for clutRenderAddress_ != 0xFFFFFFFF and zero the CLUT or something,
|
||||
// but choosing not to for now. Though the results of loading the CLUT from RAM here is
|
||||
// almost certainly going to be bogus.
|
||||
#ifdef _M_SSE
|
||||
if (bytes == loadBytes) {
|
||||
const __m128i *source = (const __m128i *)Memory::GetPointerUnchecked(clutAddr);
|
||||
__m128i *dest = (__m128i *)clutBufRaw_;
|
||||
int numBlocks = bytes / 32;
|
||||
for (int i = 0; i < numBlocks; i++, source += 2, dest += 2) {
|
||||
__m128i data1 = _mm_loadu_si128(source);
|
||||
__m128i data2 = _mm_loadu_si128(source + 1);
|
||||
_mm_store_si128(dest, data1);
|
||||
_mm_store_si128(dest + 1, data2);
|
||||
}
|
||||
} else {
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
}
|
||||
#elif PPSSPP_ARCH(ARM_NEON)
|
||||
if (bytes == loadBytes) {
|
||||
const uint32_t *source = (const uint32_t *)Memory::GetPointerUnchecked(clutAddr);
|
||||
uint32_t *dest = (uint32_t *)clutBufRaw_;
|
||||
int numBlocks = bytes / 32;
|
||||
for (int i = 0; i < numBlocks; i++, source += 8, dest += 8) {
|
||||
uint32x4_t data1 = vld1q_u32(source);
|
||||
uint32x4_t data2 = vld1q_u32(source + 4);
|
||||
vst1q_u32(dest, data1);
|
||||
vst1q_u32(dest + 4, data2);
|
||||
}
|
||||
} else {
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
// We'll need to copy from the offset.
|
||||
const u32 fb_bpp = BufferFormatBytesPerPixel(chosenFramebuffer->fb_format);
|
||||
const int totalPixelsOffset = clutRenderOffset_ / fb_bpp;
|
||||
const int clutYOffset = totalPixelsOffset / chosenFramebuffer->fb_stride;
|
||||
const int clutXOffset = totalPixelsOffset % chosenFramebuffer->fb_stride;
|
||||
const int scale = chosenFramebuffer->renderScaleFactor;
|
||||
|
||||
// Copy the pixels to our temp clut, scaling down if needed and wrapping.
|
||||
framebufferManager_->BlitUsingRaster(
|
||||
chosenFramebuffer->fbo, clutXOffset * scale, clutYOffset * scale, (clutXOffset + 512.0f) * scale, (clutYOffset + 1.0f) * scale,
|
||||
dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f,
|
||||
false, scale, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR_RECT2LIN), "copy_clut_to_temp");
|
||||
|
||||
framebufferManager_->RebindFramebuffer("after_copy_clut_to_temp");
|
||||
clutRenderFormat_ = chosenFramebuffer->fb_format;
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::ALLOC, clutAddr, loadBytes, "CLUT");
|
||||
}
|
||||
|
||||
// It's possible for a game to load CLUT outside valid memory without crashing, should result in zeroes.
|
||||
u32 bytes = Memory::ValidSize(clutAddr, loadBytes);
|
||||
_assert_(bytes <= 2048);
|
||||
bool performDownload = PSP_CoreParameter().compat.flags().AllowDownloadCLUT;
|
||||
if (GPURecord::IsActive())
|
||||
performDownload = true;
|
||||
if (clutRenderAddress_ != 0xFFFFFFFF && performDownload) {
|
||||
framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes);
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
} else {
|
||||
memset(clutBufRaw_, 0x00, loadBytes);
|
||||
// Here we could check for clutRenderAddress_ != 0xFFFFFFFF and zero the CLUT or something,
|
||||
// but choosing not to for now. Though the results of loading the CLUT from RAM here is
|
||||
// almost certainly going to be bogus.
|
||||
#ifdef _M_SSE
|
||||
if (bytes == loadBytes) {
|
||||
const __m128i *source = (const __m128i *)Memory::GetPointerUnchecked(clutAddr);
|
||||
__m128i *dest = (__m128i *)clutBufRaw_;
|
||||
int numBlocks = bytes / 32;
|
||||
for (int i = 0; i < numBlocks; i++, source += 2, dest += 2) {
|
||||
__m128i data1 = _mm_loadu_si128(source);
|
||||
__m128i data2 = _mm_loadu_si128(source + 1);
|
||||
_mm_store_si128(dest, data1);
|
||||
_mm_store_si128(dest + 1, data2);
|
||||
}
|
||||
} else {
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
}
|
||||
#elif PPSSPP_ARCH(ARM_NEON)
|
||||
if (bytes == loadBytes) {
|
||||
const uint32_t *source = (const uint32_t *)Memory::GetPointerUnchecked(clutAddr);
|
||||
uint32_t *dest = (uint32_t *)clutBufRaw_;
|
||||
int numBlocks = bytes / 32;
|
||||
for (int i = 0; i < numBlocks; i++, source += 8, dest += 8) {
|
||||
uint32x4_t data1 = vld1q_u32(source);
|
||||
uint32x4_t data2 = vld1q_u32(source + 4);
|
||||
vst1q_u32(dest, data1);
|
||||
vst1q_u32(dest + 4, data2);
|
||||
}
|
||||
} else {
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reload the clut next time.
|
||||
clutLastFormat_ = 0xFFFFFFFF;
|
||||
clutMaxBytes_ = std::max(clutMaxBytes_, loadBytes);
|
||||
|
||||
@@ -50,10 +50,7 @@ FramebufferManagerDX9::FramebufferManagerDX9(Draw::DrawContext *draw)
|
||||
preferredPixelsFormat_ = Draw::DataFormat::B8G8R8A8_UNORM;
|
||||
}
|
||||
|
||||
FramebufferManagerDX9::~FramebufferManagerDX9() {
|
||||
}
|
||||
|
||||
bool FramebufferManagerDX9::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH) {
|
||||
bool FramebufferManagerDX9::ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH, Draw::ReadbackMode mode) {
|
||||
// Don't yet support stretched readbacks here.
|
||||
if (destW != w || destH != h) {
|
||||
return false;
|
||||
|
||||
@@ -31,9 +31,8 @@ class ShaderManagerDX9;
|
||||
class FramebufferManagerDX9 : public FramebufferManagerCommon {
|
||||
public:
|
||||
FramebufferManagerDX9(Draw::DrawContext *draw);
|
||||
~FramebufferManagerDX9();
|
||||
|
||||
protected:
|
||||
// TODO: The non-color path of FramebufferManagerCommon::ReadbackDepthbufferSync seems to work just as well.
|
||||
bool ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH) override;
|
||||
bool ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH, Draw::ReadbackMode mode) override;
|
||||
};
|
||||
@@ -36,5 +36,5 @@ public:
|
||||
|
||||
protected:
|
||||
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
|
||||
bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) override;
|
||||
bool ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride, Draw::ReadbackMode mode) override;
|
||||
};
|
||||
@@ -98,7 +98,7 @@ void main() {
|
||||
Draw::Pipeline *CreateReadbackPipeline(Draw::DrawContext *draw, const char *tag, const UniformBufferDesc *ubDesc, const char *fs, const char *fsTag, const char *vs, const char *vsTag);
|
||||
|
||||
// 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) {
|
||||
bool FramebufferManagerGLES::ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride, Draw::ReadbackMode mode) {
|
||||
using namespace Draw;
|
||||
|
||||
if (!fbo) {
|
||||
@@ -150,7 +150,7 @@ bool FramebufferManagerGLES::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, i
|
||||
};
|
||||
draw_->DrawUP(positions, 3);
|
||||
|
||||
draw_->CopyFramebufferToMemory(blitFBO, FB_COLOR_BIT, x, y, w, h, DataFormat::R8G8B8A8_UNORM, convBuf_, w, ReadbackMode::BLOCK, "ReadbackStencilbufferSync");
|
||||
draw_->CopyFramebufferToMemory(blitFBO, FB_COLOR_BIT, x, y, w, h, DataFormat::R8G8B8A8_UNORM, convBuf_, w, mode, "ReadbackStencilbufferSync");
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
|
||||
|
||||
Reference in new issue
Block a user