mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-26 08:55:58 +00:00
Merge pull request #9713 from hrydgard/vulkan-improvements
Vulkan - initial implementation of buffered rendering, fixes for Mali/Android
This commit is contained in:
commit
c109f849e0
@ -86,6 +86,7 @@ VulkanContext::VulkanContext(const char *app_name, int app_ver, uint32_t flags)
|
||||
instance_extension_names.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
#endif
|
||||
device_extension_names.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
// device_extension_names.push_back(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
|
||||
if (flags & VULKAN_FLAG_VALIDATE) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(validationLayers); i++) {
|
||||
@ -215,13 +216,11 @@ void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) {
|
||||
cmdQueue_.push_back(cmd);
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
|
||||
VkCommandBuffer VulkanContext::BeginFrame() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
|
||||
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
|
||||
// Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine.
|
||||
VkResult res = vkAcquireNextImageKHR(device_, swap_chain_, UINT64_MAX, acquireSemaphore, VK_NULL_HANDLE, ¤t_buffer);
|
||||
|
||||
// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
|
||||
// return codes
|
||||
assert(res == VK_SUCCESS);
|
||||
@ -238,7 +237,11 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
|
||||
res = vkBeginCommandBuffer(frame->cmdBuf, &begin);
|
||||
|
||||
TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
|
||||
return frame->cmdBuf;
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
||||
rp_begin.renderPass = surface_render_pass_;
|
||||
rp_begin.framebuffer = framebuffers_[current_buffer];
|
||||
@ -248,18 +251,18 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
|
||||
rp_begin.renderArea.extent.height = height_;
|
||||
rp_begin.clearValueCount = 2;
|
||||
rp_begin.pClearValues = clear_values;
|
||||
|
||||
// We don't really need to record this at this point in time, but hey, at some point we'll start this
|
||||
// pass anyway so might as well do it now (although you can imagine getting away with just a stretchblt and not
|
||||
// even starting a final render pass if there's nothing to overlay... hm. Uncommon though on mobile).
|
||||
vkCmdBeginRenderPass(frame->cmdBuf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
return frame->cmdBuf;
|
||||
}
|
||||
|
||||
void VulkanContext::EndSurfaceRenderPass() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
// ILOG("VulkanContext::EndSurfaceRenderPass");
|
||||
vkCmdEndRenderPass(frame->cmdBuf);
|
||||
}
|
||||
|
||||
void VulkanContext::EndFrame() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
TransitionToPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
|
||||
|
||||
VkResult res = vkEndCommandBuffer(frame->cmdBuf);
|
||||
@ -285,12 +288,12 @@ void VulkanContext::EndSurfaceRenderPass() {
|
||||
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &acquireSemaphore;
|
||||
VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
|
||||
VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT };
|
||||
submit_info.pWaitDstStageMask = waitStage;
|
||||
submit_info.commandBufferCount = (uint32_t)cmdBufs.size();
|
||||
submit_info.pCommandBuffers = cmdBufs.data();
|
||||
submit_info.signalSemaphoreCount = 0;
|
||||
submit_info.pSignalSemaphores = NULL;
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &renderingCompleteSemaphore;
|
||||
res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
@ -298,8 +301,8 @@ void VulkanContext::EndSurfaceRenderPass() {
|
||||
present.swapchainCount = 1;
|
||||
present.pSwapchains = &swap_chain_;
|
||||
present.pImageIndices = ¤t_buffer;
|
||||
present.pWaitSemaphores = NULL;
|
||||
present.waitSemaphoreCount = 0;
|
||||
present.pWaitSemaphores = &renderingCompleteSemaphore;
|
||||
present.waitSemaphoreCount = 1;
|
||||
present.pResults = NULL;
|
||||
|
||||
res = vkQueuePresentKHR(gfx_queue_, &present);
|
||||
@ -942,13 +945,11 @@ void VulkanContext::InitQueue() {
|
||||
vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_);
|
||||
ILOG("gfx_queue_: %p", gfx_queue_);
|
||||
|
||||
VkSemaphoreCreateInfo acquireSemaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
acquireSemaphoreCreateInfo.flags = 0;
|
||||
|
||||
res = vkCreateSemaphore(device_,
|
||||
&acquireSemaphoreCreateInfo,
|
||||
NULL,
|
||||
&acquireSemaphore);
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, NULL, &acquireSemaphore);
|
||||
assert(res == VK_SUCCESS);
|
||||
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
@ -1063,7 +1064,9 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
||||
&swapchainImageCount, NULL);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkImage* swapchainImages = (VkImage*)malloc(swapchainImageCount * sizeof(VkImage));
|
||||
ILOG("Vulkan swapchain image count: %d", swapchainImageCount);
|
||||
|
||||
VkImage* swapchainImages = new VkImage[swapchainImageCount];
|
||||
assert(swapchainImages);
|
||||
res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, swapchainImages);
|
||||
assert(res == VK_SUCCESS);
|
||||
@ -1101,8 +1104,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
||||
swapChainBuffers.push_back(sc_buffer);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
free(swapchainImages);
|
||||
|
||||
delete[] swapchainImages;
|
||||
current_buffer = 0;
|
||||
|
||||
return true;
|
||||
@ -1242,6 +1244,7 @@ void VulkanContext::DestroySwapChain() {
|
||||
swap_chain_ = VK_NULL_HANDLE;
|
||||
swapChainBuffers.clear();
|
||||
vkDestroySemaphore(device_, acquireSemaphore, NULL);
|
||||
vkDestroySemaphore(device_, renderingCompleteSemaphore, NULL);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyFramebuffers() {
|
||||
@ -1300,6 +1303,9 @@ void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlag
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
|
||||
image_memory_barrier.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
|
||||
}
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
||||
image_memory_barrier.srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||
image_memory_barrier.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
@ -258,17 +258,22 @@ public:
|
||||
|
||||
VkCommandBuffer GetInitCommandBuffer();
|
||||
|
||||
VkFramebuffer GetSurfaceFramebuffer() {
|
||||
return framebuffers_[current_buffer];
|
||||
}
|
||||
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
|
||||
VkCommandBuffer GetSurfaceCommandBuffer() {
|
||||
return frame_[curFrame_ & 1].cmdBuf;
|
||||
}
|
||||
|
||||
VkCommandBuffer BeginFrame();
|
||||
// The surface render pass is special because it has to acquire the backbuffer, and may thus "block".
|
||||
// Use the returned command buffer to enqueue commands that render to the backbuffer.
|
||||
// To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd).
|
||||
VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]);
|
||||
// May eventually need the ability to break and resume the backbuffer render pass in a few rare cases.
|
||||
void EndSurfaceRenderPass();
|
||||
void EndFrame();
|
||||
|
||||
void QueueBeforeSurfaceRender(VkCommandBuffer cmd);
|
||||
|
||||
@ -312,6 +317,7 @@ public:
|
||||
|
||||
private:
|
||||
VkSemaphore acquireSemaphore;
|
||||
VkSemaphore renderingCompleteSemaphore;
|
||||
|
||||
#ifdef _WIN32
|
||||
HINSTANCE connection; // hInstance - Windows Instance
|
||||
|
@ -362,6 +362,14 @@ void VulkanTexture::EndCreate() {
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
void VulkanTexture::TransitionForUpload() {
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
TransitionImageLayout(cmd, image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
|
||||
void VulkanTexture::Destroy() {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vulkan_->Delete().QueueDeleteImageView(view);
|
||||
|
@ -34,6 +34,9 @@ public:
|
||||
bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
|
||||
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
|
||||
void EndCreate();
|
||||
|
||||
void TransitionForUpload();
|
||||
|
||||
int GetNumMips() const { return numMips_; }
|
||||
void Destroy();
|
||||
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
void Unmap() {
|
||||
assert(writePtr_);
|
||||
/*
|
||||
// Should not need this since we use coherent memory.
|
||||
VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
|
||||
range.offset = 0;
|
||||
range.size = offset_;
|
||||
|
@ -134,10 +134,6 @@ FramebufferManagerCommon::~FramebufferManagerCommon() {
|
||||
|
||||
void FramebufferManagerCommon::Init() {
|
||||
const std::string gameId = g_paramSFO.GetValueString("DISC_ID");
|
||||
// And an initial clear. We don't clear per frame as the games are supposed to handle that
|
||||
// by themselves.
|
||||
ClearBuffer();
|
||||
|
||||
BeginFrame();
|
||||
}
|
||||
|
||||
@ -204,14 +200,9 @@ void FramebufferManagerCommon::SetNumExtraFBOs(int num) {
|
||||
// No depth/stencil for post processing
|
||||
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ (int)renderWidth_, (int)renderHeight_, 1, 1, false, Draw::FBO_8888 });
|
||||
extraFBOs_.push_back(fbo);
|
||||
|
||||
// The new FBO is still bound after creation, but let's bind it anyway.
|
||||
draw_->BindFramebufferAsRenderTarget(fbo);
|
||||
ClearBuffer();
|
||||
}
|
||||
|
||||
currentRenderVfb_ = 0;
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
|
||||
// Heuristics to figure out the size of FBO to create.
|
||||
@ -533,13 +524,13 @@ void FramebufferManagerCommon::DestroyFramebuf(VirtualFramebuffer *v) {
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb) {
|
||||
if (!useBufferedRendering_) {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
// Let's ignore rendering to targets that have not (yet) been displayed.
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB;
|
||||
}
|
||||
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED);
|
||||
|
||||
// TODO: Is this necessary?
|
||||
ClearBuffer();
|
||||
|
||||
// ugly...
|
||||
@ -579,41 +570,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
|
||||
}
|
||||
textureCache_->ForgetLastTexture();
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
} else {
|
||||
// This should only happen very briefly when toggling useBufferedRendering_.
|
||||
ResizeFramebufFBO(vfb, vfb->width, vfb->height, true);
|
||||
}
|
||||
} else {
|
||||
if (vfb->fbo) {
|
||||
// This should only happen very briefly when toggling useBufferedRendering_.
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_DESTROYED);
|
||||
delete vfb->fbo;
|
||||
vfb->fbo = nullptr;
|
||||
}
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
|
||||
// Let's ignore rendering to targets that have not (yet) been displayed.
|
||||
if (vfb->usageFlags & FB_USAGE_DISPLAYED_FRAMEBUFFER) {
|
||||
gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB;
|
||||
} else {
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB;
|
||||
}
|
||||
}
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_UPDATED);
|
||||
|
||||
if (gl_extensions.IsGLES) {
|
||||
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
|
||||
// to it. This broke stuff before, so now it only clears on the first use of an
|
||||
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
|
||||
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
|
||||
if (vfb->last_frame_render != gpuStats.numFlips) {
|
||||
ClearBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy depth pixel value from the read framebuffer to the draw framebuffer
|
||||
if (prevVfb && !g_Config.bDisableSlowFramebufEffects) {
|
||||
if (!prevVfb->fbo || !vfb->fbo || !useBufferedRendering_ || !prevVfb->depthUpdated || isClearingDepth) {
|
||||
@ -628,6 +584,42 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
|
||||
ReformatFramebufferFrom(vfb, vfb->drawnFormat);
|
||||
}
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (vfb->fbo) {
|
||||
if (gl_extensions.IsGLES) {
|
||||
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
|
||||
// to it. This broke stuff before, so now it only clears on the first use of an
|
||||
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
|
||||
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
|
||||
if (vfb->last_frame_render != gpuStats.numFlips) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
} else {
|
||||
// This should only happen very briefly when toggling useBufferedRendering_.
|
||||
ResizeFramebufFBO(vfb, vfb->width, vfb->height, true);
|
||||
}
|
||||
} else {
|
||||
if (vfb->fbo) {
|
||||
// This should only happen very briefly when toggling useBufferedRendering_.
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_DESTROYED);
|
||||
delete vfb->fbo;
|
||||
vfb->fbo = nullptr;
|
||||
}
|
||||
|
||||
// Let's ignore rendering to targets that have not (yet) been displayed.
|
||||
if (vfb->usageFlags & FB_USAGE_DISPLAYED_FRAMEBUFFER) {
|
||||
gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB;
|
||||
} else {
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB;
|
||||
}
|
||||
}
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_UPDATED);
|
||||
|
||||
// ugly...
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX);
|
||||
@ -710,8 +702,9 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
|
||||
|
||||
if (useBufferedRendering_ && vfb && vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
|
||||
draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight);
|
||||
} else {
|
||||
// We are drawing to the back buffer so need to flip.
|
||||
if (needBackBufferYSwap_)
|
||||
@ -719,6 +712,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
float x, y, w, h;
|
||||
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
SetViewport2D(x, y, w, h);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
}
|
||||
DisableState();
|
||||
|
||||
@ -827,21 +821,17 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) {
|
||||
void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
DownloadFramebufferOnSwitch(currentRenderVfb_);
|
||||
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
currentRenderVfb_ = 0;
|
||||
|
||||
if (displayFramebufPtr_ == 0) {
|
||||
DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black");
|
||||
// No framebuffer to display! Clear to black.
|
||||
ClearBuffer();
|
||||
if (useBufferedRendering_) {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
draw_->Clear(Draw::FB_COLOR_BIT | Draw::FB_STENCIL_BIT | Draw::FB_DEPTH_BIT, 0, 0, 0);
|
||||
}
|
||||
|
||||
u32 offsetX = 0;
|
||||
u32 offsetY = 0;
|
||||
|
||||
@ -900,14 +890,23 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
}
|
||||
|
||||
if (!vfb) {
|
||||
if (useBufferedRendering_) {
|
||||
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
// Just a pointer to plain memory to draw. We should create a framebuffer, then draw to it.
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
DEBUG_LOG(FRAMEBUF, "Found no FBO to display! displayFBPtr = %08x", displayFramebufPtr_);
|
||||
// No framebuffer to display! Clear to black.
|
||||
ClearBuffer();
|
||||
if (useBufferedRendering_) {
|
||||
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -929,8 +928,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
DEBUG_LOG(FRAMEBUF, "Displaying FBO %08x", vfb->fb_address);
|
||||
DisableState();
|
||||
|
||||
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
|
||||
|
||||
int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
|
||||
// Output coordinates
|
||||
@ -945,6 +942,10 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
float v1 = (272.0f + offsetY) / (float)vfb->bufferHeight;
|
||||
|
||||
if (!usePostShader_) {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
bool linearFilter = g_Config.iBufFilter == SCALE_LINEAR;
|
||||
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
|
||||
// flip V.
|
||||
@ -966,10 +967,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
}
|
||||
} else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) {
|
||||
// An additional pass, post-processing shader to the extra FBO.
|
||||
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0]);
|
||||
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0], { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
int fbo_w, fbo_h;
|
||||
draw_->GetFramebufferDimensions(extraFBOs_[0], &fbo_w, &fbo_h);
|
||||
SetViewport2D(0, 0, fbo_w, fbo_h);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
shaderManager_->DirtyLastShader(); // dirty lastShader_
|
||||
PostShaderUniforms uniforms{};
|
||||
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms);
|
||||
@ -977,7 +979,8 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
bool linearFilter = g_Config.iBufFilter == SCALE_LINEAR;
|
||||
DrawActiveTexture(0, 0, fbo_w, fbo_h, fbo_w, fbo_h, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, linearFilter);
|
||||
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
|
||||
// Use the extra FBO, with applied post-processing shader, as a texture.
|
||||
// fbo_bind_as_texture(extraFBOs_[0], FB_COLOR_BIT, 0);
|
||||
@ -1009,11 +1012,13 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
|
||||
/*
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0]);
|
||||
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0], { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
}*/
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
|
||||
// flip V.
|
||||
if (needBackBufferYSwap_)
|
||||
@ -1045,9 +1050,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::DecimateFBOs() {
|
||||
if (useBufferedRendering_) {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
}
|
||||
currentRenderVfb_ = 0;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
@ -1138,7 +1140,6 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w,
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
|
||||
if (!useBufferedRendering_) {
|
||||
if (vfb->fbo) {
|
||||
@ -1156,16 +1157,17 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w,
|
||||
if (old.fbo) {
|
||||
INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, w, h, vfb->format);
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
ClearBuffer();
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
if (!skipCopy && !g_Config.bDisableSlowFramebufEffects) {
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min(vfb->bufferWidth, vfb->width), std::min(vfb->height, vfb->bufferHeight), 0);
|
||||
}
|
||||
}
|
||||
delete old.fbo;
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
if (needGLESRebinds_) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
|
||||
if (!vfb->fbo) {
|
||||
@ -1801,8 +1803,9 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBCo
|
||||
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, false, depth });
|
||||
if (!fbo)
|
||||
return fbo;
|
||||
draw_->BindFramebufferAsRenderTarget(fbo);
|
||||
ClearBuffer(true);
|
||||
|
||||
// TODO: Move binding out of here!
|
||||
draw_->BindFramebufferAsRenderTarget(fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
const TempFBO info = { fbo, gpuStats.numFlips };
|
||||
tempFBOs_[key] = info;
|
||||
return fbo;
|
||||
|
@ -267,7 +267,7 @@ public:
|
||||
|
||||
virtual void Resized();
|
||||
|
||||
Draw::Framebuffer *GetTempFBO(u16 w, u16 h, Draw::FBColorDepth depth = Draw::FBO_8888);
|
||||
Draw::Framebuffer *GetTempFBO(u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888);
|
||||
|
||||
// Debug features
|
||||
virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) = 0;
|
||||
@ -375,6 +375,7 @@ protected:
|
||||
// Used by post-processing shaders
|
||||
std::vector<Draw::Framebuffer *> extraFBOs_;
|
||||
|
||||
bool needGLESRebinds_ = false;
|
||||
|
||||
struct TempFBO {
|
||||
Draw::Framebuffer *fbo;
|
||||
|
@ -218,8 +218,6 @@ void FramebufferManagerD3D11::DisableState() {
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::CompilePostShader() {
|
||||
SetNumExtraFBOs(0);
|
||||
|
||||
std::string vsSource;
|
||||
std::string psSource;
|
||||
|
||||
@ -472,9 +470,10 @@ void FramebufferManagerD3D11::BindPostShader(const PostShaderUniforms &uniforms)
|
||||
|
||||
void FramebufferManagerD3D11::RebindFramebuffer() {
|
||||
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
} else {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
// Should this even happen?
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,8 +482,6 @@ void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, G
|
||||
return;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
@ -493,8 +490,10 @@ void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, G
|
||||
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
|
||||
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP });
|
||||
|
||||
// TODO: There's no way this does anything useful :(
|
||||
context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF);
|
||||
context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF);
|
||||
context_->RSSetState(stockD3D11.rasterStateNoCull);
|
||||
@ -571,7 +570,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr
|
||||
if (renderCopy) {
|
||||
VirtualFramebuffer copyInfo = *framebuffer;
|
||||
copyInfo.fbo = renderCopy;
|
||||
|
||||
CopyFramebufferForColorTexture(©Info, framebuffer, flags);
|
||||
RebindFramebuffer();
|
||||
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, 0);
|
||||
@ -653,8 +651,7 @@ bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb)
|
||||
return false;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo);
|
||||
ClearBuffer();
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -697,7 +694,7 @@ void FramebufferManagerD3D11::SimpleBlit(
|
||||
|
||||
// Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily).
|
||||
draw_->BindTexture(0, nullptr);
|
||||
draw_->BindFramebufferAsRenderTarget(dest);
|
||||
draw_->BindFramebufferAsRenderTarget(dest, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
draw_->BindFramebufferAsTexture(src, 0, Draw::FB_COLOR_BIT, 0);
|
||||
|
||||
Bind2DShader();
|
||||
@ -717,7 +714,9 @@ void FramebufferManagerD3D11::SimpleBlit(
|
||||
void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
|
||||
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
|
||||
// This can happen if they recently switched from non-buffered.
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
if (useBufferedRendering_) {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -812,7 +811,6 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 wid
|
||||
void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0");
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -884,7 +882,6 @@ std::vector<FramebufferInfo> FramebufferManagerD3D11::GetFramebufferList() {
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::DestroyAllFBOs() {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
currentRenderVfb_ = 0;
|
||||
displayFramebuf_ = 0;
|
||||
prevDisplayFramebuf_ = 0;
|
||||
|
@ -194,16 +194,13 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ
|
||||
float v1 = 1.0f;
|
||||
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
|
||||
if (dstBuffer->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
} else {
|
||||
// something is wrong...
|
||||
}
|
||||
D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f };
|
||||
context_->RSSetViewports(1, &vp);
|
||||
|
||||
// Zero stencil
|
||||
draw_->Clear(Draw::FBChannel::FB_STENCIL_BIT, 0, 0, 0);
|
||||
|
||||
float fw = dstBuffer->width;
|
||||
float fh = dstBuffer->height;
|
||||
|
||||
|
@ -434,7 +434,7 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFra
|
||||
context_->PSSetShaderResources(1, 1, &clutTexture);
|
||||
framebufferManagerD3D11_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY);
|
||||
context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DWrap);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
shaderApply.Shade();
|
||||
|
||||
framebufferManagerD3D11_->RebindFramebuffer();
|
||||
@ -453,7 +453,7 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFra
|
||||
|
||||
gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650);
|
||||
gstate_c.SetTextureSimpleAlpha(gstate_c.textureFullAlpha);
|
||||
framebufferManagerD3D11_->RebindFramebuffer();
|
||||
framebufferManagerD3D11_->RebindFramebuffer(); // Probably not necessary.
|
||||
}
|
||||
SamplerCacheKey samplerKey;
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey);
|
||||
|
@ -324,9 +324,10 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
|
||||
void FramebufferManagerDX9::RebindFramebuffer() {
|
||||
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
} else {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
// Should this even happen?
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,7 +346,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP });
|
||||
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
@ -552,8 +553,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
return false;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo);
|
||||
ClearBuffer();
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -564,7 +564,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
void FramebufferManagerDX9::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
|
||||
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
|
||||
// This can happen if we recently switched from non-buffered.
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
if (useBufferedRendering_)
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -664,7 +665,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
void FramebufferManagerDX9::PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0");
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -792,7 +792,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::DestroyAllFBOs() {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
currentRenderVfb_ = 0;
|
||||
displayFramebuf_ = 0;
|
||||
prevDisplayFramebuf_ = 0;
|
||||
@ -882,8 +881,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
|
||||
bool FramebufferManagerDX9::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
|
||||
LPDIRECT3DSURFACE9 renderTarget = nullptr;
|
||||
HRESULT hr = device_->GetRenderTarget(0, &renderTarget);
|
||||
bool success = false;
|
||||
@ -899,7 +896,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
renderTarget->Release();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer
|
||||
u16 h = dstBuffer->renderHeight;
|
||||
|
||||
if (dstBuffer->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
}
|
||||
D3DVIEWPORT9 vp{ 0, 0, w, h, 0.0f, 1.0f };
|
||||
device_->SetViewport(&vp);
|
||||
|
@ -421,7 +421,7 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame
|
||||
LPDIRECT3DTEXTURE9 clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
|
||||
Draw::Framebuffer *depalFBO = framebufferManagerDX9_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
float xoff = -0.5f / framebuffer->renderWidth;
|
||||
|
@ -645,6 +645,8 @@ void DrawEngineGLES::DoFlush() {
|
||||
PROFILE_THIS_SCOPE("flush");
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
|
||||
|
||||
gpuStats.numFlushes++;
|
||||
gpuStats.numTrackedVertexArrays = (int)vai_.size();
|
||||
|
||||
|
@ -234,6 +234,7 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) :
|
||||
currentPBO_(0)
|
||||
{
|
||||
needBackBufferYSwap_ = true;
|
||||
needGLESRebinds_ = true;
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::Init() {
|
||||
@ -450,9 +451,10 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
|
||||
|
||||
void FramebufferManagerGLES::RebindFramebuffer() {
|
||||
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
} else {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
// Should this even happen?
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE)
|
||||
glstate.viewport.restore();
|
||||
@ -475,8 +477,6 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE
|
||||
return;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
@ -487,14 +487,9 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
glstate.scissorTest.disable();
|
||||
glstate.depthWrite.set(GL_FALSE);
|
||||
glstate.colorMask.set(false, false, false, true);
|
||||
glstate.stencilFunc.set(GL_ALWAYS, 0, 0);
|
||||
glstate.stencilMask.set(0xFF);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClearStencil(0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
|
||||
RebindFramebuffer();
|
||||
@ -655,10 +650,6 @@ bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb)
|
||||
ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
|
||||
return false;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo);
|
||||
ClearBuffer();
|
||||
glDisable(GL_DITHER); // Weird place to do this
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -667,11 +658,11 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
|
||||
|
||||
// Discard the previous contents of this buffer where possible.
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
} else if (gl_extensions.IsGLES) {
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
ClearBuffer();
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
@ -680,7 +671,8 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
|
||||
void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
|
||||
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
|
||||
// This can happen if they recently switched from non-buffered.
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
if (useBufferedRendering_)
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -735,7 +727,7 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
|
||||
if (useBlit) {
|
||||
draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST);
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(dst->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(dst->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
draw_->BindFramebufferAsTexture(src->fbo, 0, Draw::FB_COLOR_BIT, 0);
|
||||
|
||||
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
|
||||
@ -1093,7 +1085,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
#ifdef USING_GLES2
|
||||
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
const GLenum target = GL_FRAMEBUFFER;
|
||||
#else
|
||||
const GLenum target = GL_READ_FRAMEBUFFER;
|
||||
@ -1161,11 +1153,11 @@ void FramebufferManagerGLES::EndFrame() {
|
||||
continue;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(temp.second.fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(temp.second.fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
}
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP , Draw::RPAction::KEEP });
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
@ -1196,7 +1188,6 @@ std::vector<FramebufferInfo> FramebufferManagerGLES::GetFramebufferList() {
|
||||
|
||||
void FramebufferManagerGLES::DestroyAllFBOs() {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
currentRenderVfb_ = 0;
|
||||
displayFramebuf_ = 0;
|
||||
prevDisplayFramebuf_ = 0;
|
||||
@ -1220,7 +1211,6 @@ void FramebufferManagerGLES::DestroyAllFBOs() {
|
||||
}
|
||||
tempFBOs_.clear();
|
||||
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
DisableState();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
@ -660,7 +660,7 @@ void GPU_GLES::Execute_Prim(u32 op, u32 diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
// This also makes skipping drawing very effective. This function can change the framebuffer.
|
||||
framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
|
@ -183,9 +183,9 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
|
||||
Draw::Framebuffer *blitFBO = nullptr;
|
||||
if (useBlit) {
|
||||
blitFBO = GetTempFBO(w, h, Draw::FBO_8888);
|
||||
draw_->BindFramebufferAsRenderTarget(blitFBO);
|
||||
draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
} else if (dstBuffer->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
}
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
|
@ -483,7 +483,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
TextureShaderApplier shaderApply(depal, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight);
|
||||
|
@ -21,7 +21,8 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{457F45D2-556F-47BC-A31D-AFF0D15BEAED}</ProjectGuid>
|
||||
<RootNamespace>GPU</RootNamespace>
|
||||
<WindowsTargetPlatformVersion></WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>
|
||||
</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
|
@ -408,9 +408,6 @@
|
||||
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\GPU_Vulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
@ -510,5 +507,8 @@
|
||||
<ClCompile Include="Common\ShaderCommon.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -205,7 +205,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
|
||||
u1 = 1.0f;
|
||||
}
|
||||
if (!hasImage) {
|
||||
draw_->Clear(Draw::FB_COLOR_BIT, 0, 0, 0);
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -236,8 +236,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
|
||||
if (GetGPUBackend() == GPUBackend::VULKAN) {
|
||||
std::swap(v0, v1);
|
||||
}
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
draw_->Clear(Draw::FB_COLOR_BIT, 0, 0, 0);
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE });
|
||||
|
||||
Draw::SamplerState *sampler;
|
||||
if (g_Config.iBufFilter == SCALE_NEAREST) {
|
||||
|
@ -64,10 +64,10 @@ enum {
|
||||
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex)
|
||||
};
|
||||
|
||||
DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
|
||||
DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw)
|
||||
: vulkan_(vulkan),
|
||||
draw_(draw),
|
||||
prevPrim_(GE_PRIM_INVALID),
|
||||
lastVTypeID_(-1),
|
||||
numDrawCalls(0),
|
||||
vertexCountInDrawCalls(0),
|
||||
curFrame_(0),
|
||||
@ -149,7 +149,7 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
VkDescriptorPoolSize dpTypes[2];
|
||||
dpTypes[0].descriptorCount = 2048;
|
||||
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
dpTypes[1].descriptorCount = 2048;
|
||||
dpTypes[1].descriptorCount = 4096;
|
||||
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
||||
VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
@ -168,7 +168,6 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
if (res == VK_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Let's try to reduce the counts.
|
||||
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
dpTypes[0].descriptorCount /= 2;
|
||||
@ -328,15 +327,17 @@ inline void DrawEngineVulkan::SetupVertexDecoderInternal(u32 vertType) {
|
||||
const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24);
|
||||
|
||||
// If vtype has changed, setup the vertex decoder.
|
||||
if (vertTypeID != lastVTypeID_) {
|
||||
if (vertTypeID != lastVType_) {
|
||||
dec_ = GetVertexDecoder(vertTypeID);
|
||||
lastVTypeID_ = vertTypeID;
|
||||
lastVType_ = vertTypeID;
|
||||
}
|
||||
if (!dec_)
|
||||
Crash();
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX)
|
||||
Flush(cmd_);
|
||||
Flush();
|
||||
|
||||
// TODO: Is this the right thing to do?
|
||||
if (prim == GE_PRIM_KEEP_PREVIOUS) {
|
||||
@ -375,7 +376,7 @@ void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim,
|
||||
// Rendertarget == texture?
|
||||
if (!g_Config.bDisableSlowFramebufEffects) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
Flush(cmd_);
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -643,28 +644,15 @@ void DrawEngineVulkan::DirtyAllUBOs() {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
//void DrawEngineVulkan::ApplyDrawStateLate() {
|
||||
/*
|
||||
// At this point, we know if the vertices are full alpha or not.
|
||||
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
|
||||
if (!gstate.isModeClear()) {
|
||||
// TODO: Test texture?
|
||||
|
||||
if (fboTexNeedBind_) {
|
||||
// Note that this is positions, not UVs, that we need the copy from.
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
|
||||
// If we are rendering at a higher resolution, linear is probably best for the dest color.
|
||||
fboTexBound_ = true;
|
||||
fboTexNeedBind_ = false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
//}
|
||||
|
||||
// The inline wrapper in the header checks for numDrawCalls == 0d
|
||||
void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
void DrawEngineVulkan::DoFlush() {
|
||||
gpuStats.numFlushes++;
|
||||
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS);
|
||||
if (!rp)
|
||||
Crash();
|
||||
|
||||
FrameData *frame = &frame_[curFrame_ & 1];
|
||||
|
||||
bool textureNeedsApply = false;
|
||||
@ -689,6 +677,8 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
uint32_t ibOffset = 0;
|
||||
uint32_t vbOffset = 0;
|
||||
|
||||
VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS);
|
||||
|
||||
if (useHWTransform) {
|
||||
// We don't detect clears in this path, so here we can switch framebuffers if necessary.
|
||||
|
||||
@ -706,7 +696,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
}
|
||||
prim = indexGen.Prim();
|
||||
|
||||
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
||||
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
||||
if (gstate.isModeThrough()) {
|
||||
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
|
||||
} else {
|
||||
@ -724,28 +714,28 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
|
||||
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_);
|
||||
// TODO: Dirty-flag these.
|
||||
vkCmdSetScissor(cmd_, 0, 1, &dynState_.scissor);
|
||||
vkCmdSetViewport(cmd_, 0, 1, &dynState_.viewport);
|
||||
vkCmdSetScissor(cmd, 0, 1, &dynState_.scissor);
|
||||
vkCmdSetViewport(cmd, 0, 1, &dynState_.viewport);
|
||||
if (dynState_.useStencil) {
|
||||
vkCmdSetStencilWriteMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilWriteMask);
|
||||
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilCompareMask);
|
||||
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilRef);
|
||||
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilWriteMask);
|
||||
vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilCompareMask);
|
||||
vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilRef);
|
||||
}
|
||||
if (dynState_.useBlendColor) {
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState_.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
vkCmdSetBlendConstants(cmd, bc);
|
||||
}
|
||||
|
||||
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
|
||||
|
||||
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey_, dec_, vshader, fshader, true);
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true);
|
||||
if (!pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
}
|
||||
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
|
||||
|
||||
UpdateUBOs(frame);
|
||||
|
||||
@ -754,7 +744,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
const uint32_t dynamicUBOOffsets[3] = {
|
||||
baseUBOOffset, lightUBOOffset, boneUBOOffset,
|
||||
};
|
||||
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
|
||||
|
||||
int stride = dec_->GetDecVtxFmt().stride;
|
||||
|
||||
@ -763,18 +753,18 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
VkBuffer ibuf;
|
||||
ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, 2 * indexGen.VertexCount(), &ibuf);
|
||||
// TODO: Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments
|
||||
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
|
||||
vkCmdBindIndexBuffer(cmd_, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets);
|
||||
vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
|
||||
int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1;
|
||||
vkCmdDrawIndexed(cmd_, vertexCount, numInstances, 0, 0, 0);
|
||||
vkCmdDrawIndexed(cmd, vertexCount, numInstances, 0, 0, 0);
|
||||
} else {
|
||||
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
|
||||
vkCmdDraw(cmd_, vertexCount, 1, 0, 0);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets);
|
||||
vkCmdDraw(cmd, vertexCount, 1, 0, 0);
|
||||
}
|
||||
} else {
|
||||
// Decode to "decoded"
|
||||
DecodeVerts(nullptr, nullptr, nullptr);
|
||||
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
||||
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
|
||||
if (gstate.isModeThrough()) {
|
||||
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
|
||||
} else {
|
||||
@ -826,32 +816,31 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
VulkanDynamicState dynState;
|
||||
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey, dynState);
|
||||
// TODO: Dirty-flag these.
|
||||
vkCmdSetScissor(cmd_, 0, 1, &dynState.scissor);
|
||||
vkCmdSetViewport(cmd_, 0, 1, &dynState.viewport);
|
||||
vkCmdSetScissor(cmd, 0, 1, &dynState.scissor);
|
||||
vkCmdSetViewport(cmd, 0, 1, &dynState.viewport);
|
||||
if (dynState.useStencil) {
|
||||
vkCmdSetStencilWriteMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilWriteMask);
|
||||
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
|
||||
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState.stencilWriteMask);
|
||||
vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
|
||||
}
|
||||
if (result.setStencil) {
|
||||
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, result.stencilValue);
|
||||
vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, result.stencilValue);
|
||||
} else if (dynState.useStencil) {
|
||||
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
|
||||
vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
|
||||
}
|
||||
if (dynState.useBlendColor) {
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
vkCmdSetBlendConstants(cmd, bc);
|
||||
}
|
||||
|
||||
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
|
||||
|
||||
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey, dec_, vshader, fshader, false);
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey, dec_, vshader, fshader, false);
|
||||
if (!pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
}
|
||||
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
|
||||
|
||||
// Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered
|
||||
UpdateUBOs(frame);
|
||||
@ -860,7 +849,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
const uint32_t dynamicUBOOffsets[3] = {
|
||||
baseUBOOffset, lightUBOOffset, boneUBOOffset,
|
||||
};
|
||||
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
|
||||
|
||||
if (drawIndexed) {
|
||||
VkBuffer vbuf, ibuf;
|
||||
@ -868,21 +857,22 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf);
|
||||
VkDeviceSize offsets[1] = { vbOffset };
|
||||
// TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments
|
||||
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
|
||||
vkCmdBindIndexBuffer(cmd_, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdDrawIndexed(cmd_, numTrans, 1, 0, 0, 0);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets);
|
||||
vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdDrawIndexed(cmd, numTrans, 1, 0, 0, 0);
|
||||
} else {
|
||||
VkBuffer vbuf;
|
||||
vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf);
|
||||
VkDeviceSize offsets[1] = { vbOffset };
|
||||
// TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments
|
||||
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
|
||||
vkCmdDraw(cmd_, numTrans, 1, 0, 0);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets);
|
||||
vkCmdDraw(cmd, numTrans, 1, 0, 0);
|
||||
}
|
||||
} else if (result.action == SW_CLEAR) {
|
||||
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
|
||||
|
||||
// We let the framebuffer manager handle the clear. It can use renderpasses to optimize on tilers.
|
||||
// If non-buffered though, it'll just do a plain clear.
|
||||
framebufferManager_->NotifyClear(gstate.isClearModeColorMask(), gstate.isClearModeAlphaMask(), gstate.isClearModeDepthMask(), result.color, result.depth);
|
||||
|
||||
int scissorX1 = gstate.getScissorX1();
|
||||
|
@ -70,7 +70,7 @@ struct DrawEngineVulkanStats {
|
||||
// Handles transform, lighting and drawing.
|
||||
class DrawEngineVulkan : public DrawEngineCommon {
|
||||
public:
|
||||
DrawEngineVulkan(VulkanContext *vulkan);
|
||||
DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw);
|
||||
virtual ~DrawEngineVulkan();
|
||||
|
||||
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
|
||||
@ -95,23 +95,19 @@ public:
|
||||
void SetupVertexDecoderInternal(u32 vertType);
|
||||
|
||||
// So that this can be inlined
|
||||
void Flush(VkCommandBuffer cmd) {
|
||||
void Flush() {
|
||||
if (!numDrawCalls)
|
||||
return;
|
||||
DoFlush(cmd);
|
||||
DoFlush();
|
||||
}
|
||||
|
||||
bool IsCodePtrVertexDecoder(const u8 *ptr) const;
|
||||
|
||||
void DispatchFlush() override { Flush(cmd_); }
|
||||
void DispatchFlush() override { Flush(); }
|
||||
void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override {
|
||||
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
|
||||
}
|
||||
|
||||
void SetCmdBuffer(VkCommandBuffer cmd) {
|
||||
cmd_ = cmd;
|
||||
}
|
||||
|
||||
VkPipelineLayout GetPipelineLayout() const {
|
||||
return pipelineLayout_;
|
||||
}
|
||||
@ -131,7 +127,7 @@ public:
|
||||
|
||||
private:
|
||||
struct FrameData;
|
||||
|
||||
void ApplyDrawStateLate();
|
||||
void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState);
|
||||
|
||||
void InitDeviceObjects();
|
||||
@ -140,12 +136,13 @@ private:
|
||||
void DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf);
|
||||
void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts);
|
||||
|
||||
void DoFlush(VkCommandBuffer cmd);
|
||||
void DoFlush();
|
||||
void UpdateUBOs(FrameData *frame);
|
||||
|
||||
VkDescriptorSet GetDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone);
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
Draw::DrawContext *draw_;
|
||||
|
||||
// We use a single descriptor set layout for all PSP draws.
|
||||
VkDescriptorSetLayout descriptorSetLayout_;
|
||||
@ -196,20 +193,12 @@ private:
|
||||
u16 indexUpperBound;
|
||||
};
|
||||
|
||||
// This is always set to the current main command buffer of the VulkanContext.
|
||||
// In the future, we may support flushing mid-frame and more fine grained command buffer usage,
|
||||
// but for now, let's just submit a whole frame at a time. This is not compatible with some games
|
||||
// that do mid frame read-backs.
|
||||
VkCommandBuffer cmd_;
|
||||
|
||||
// Vertex collector state
|
||||
IndexGenerator indexGen;
|
||||
GEPrimitiveType prevPrim_;
|
||||
|
||||
u32 lastVTypeID_;
|
||||
|
||||
TransformedVertex *transformed;
|
||||
TransformedVertex *transformedExpanded;
|
||||
TransformedVertex *transformed = nullptr;
|
||||
TransformedVertex *transformedExpanded = nullptr;
|
||||
|
||||
// Other
|
||||
ShaderManagerVulkan *shaderManager_ = nullptr;
|
||||
|
@ -52,8 +52,6 @@
|
||||
#include "GPU/Vulkan/ShaderManagerVulkan.h"
|
||||
#include "GPU/Vulkan/VulkanUtil.h"
|
||||
|
||||
const VkFormat framebufFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
static const char tex_fs[] = R"(#version 400
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
@ -92,7 +90,6 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk
|
||||
pixelBufObj_(nullptr),
|
||||
currentPBO_(0),
|
||||
curFrame_(0),
|
||||
pipelineBasicTex_(VK_NULL_HANDLE),
|
||||
pipelinePostShader_(VK_NULL_HANDLE),
|
||||
vulkan2D_(vulkan) {
|
||||
|
||||
@ -117,74 +114,8 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) {
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::InitDeviceObjects() {
|
||||
// Create a bunch of render pass objects, for normal rendering with a depth buffer,
|
||||
// with and without pre-clearing of both depth/stencil and color, so 4 combos.
|
||||
VkAttachmentDescription attachments[2] = {};
|
||||
attachments[0].format = framebufFormat;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].flags = 0;
|
||||
|
||||
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].flags = 0;
|
||||
|
||||
VkAttachmentReference color_reference = {};
|
||||
color_reference.attachment = 0;
|
||||
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depth_reference = {};
|
||||
depth_reference.attachment = 1;
|
||||
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass = {};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.flags = 0;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = NULL;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &color_reference;
|
||||
subpass.pResolveAttachments = NULL;
|
||||
subpass.pDepthStencilAttachment = &depth_reference;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = NULL;
|
||||
|
||||
VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp.attachmentCount = 2;
|
||||
rp.pAttachments = attachments;
|
||||
rp.subpassCount = 1;
|
||||
rp.pSubpasses = &subpass;
|
||||
rp.dependencyCount = 0;
|
||||
rp.pDependencies = NULL;
|
||||
|
||||
// TODO: Maybe LOAD_OP_DONT_CARE makes sense in some situations. Additionally,
|
||||
// there is often no need to store the depth buffer afterwards, although hard to know up front.
|
||||
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorLoadDepth_);
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorLoadDepth_);
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorClearDepth_);
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorClearDepth_);
|
||||
|
||||
// Initialize framedata
|
||||
for (int i = 0; i < 2; i++) {
|
||||
VkCommandPoolCreateInfo cp = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
cp.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
cp.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex();
|
||||
VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cp, nullptr, &frameData_[i].cmdPool_);
|
||||
assert(res == VK_SUCCESS);
|
||||
frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024);
|
||||
}
|
||||
|
||||
@ -196,7 +127,9 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
|
||||
assert(fsBasicTex_ != VK_NULL_HANDLE);
|
||||
assert(vsBasicTex_ != VK_NULL_HANDLE);
|
||||
|
||||
pipelineBasicTex_ = vulkan2D_.GetPipeline(pipelineCache2D_, rpClearColorClearDepth_, vsBasicTex_, fsBasicTex_);
|
||||
// Prime the 2D pipeline cache.
|
||||
vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_);
|
||||
vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_);
|
||||
|
||||
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
@ -213,25 +146,7 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DestroyDeviceObjects() {
|
||||
if (rpLoadColorLoadDepth_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorLoadDepth_);
|
||||
if (rpClearColorLoadDepth_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorLoadDepth_);
|
||||
if (rpClearColorClearDepth_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorClearDepth_);
|
||||
if (rpLoadColorClearDepth_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorClearDepth_);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (frameData_[i].numCommandBuffers_ > 0) {
|
||||
vkFreeCommandBuffers(vulkan_->GetDevice(), frameData_[i].cmdPool_, frameData_[i].numCommandBuffers_, frameData_[i].commandBuffers_);
|
||||
frameData_[i].numCommandBuffers_ = 0;
|
||||
frameData_[i].totalCommandBuffers_ = 0;
|
||||
}
|
||||
if (frameData_[i].cmdPool_ != VK_NULL_HANDLE) {
|
||||
vkDestroyCommandPool(vulkan_->GetDevice(), frameData_[i].cmdPool_, nullptr);
|
||||
frameData_[i].cmdPool_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (frameData_[i].push_) {
|
||||
frameData_[i].push_->Destroy(vulkan_);
|
||||
delete frameData_[i].push_;
|
||||
@ -256,50 +171,30 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() {
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) {
|
||||
if (!useBufferedRendering_) {
|
||||
// if (!useBufferedRendering_) {
|
||||
float x, y, w, h;
|
||||
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
|
||||
VkClearValue colorValue, depthValue;
|
||||
Uint8x4ToFloat4(colorValue.color.float32, color);
|
||||
depthValue.depthStencil.depth = depth;
|
||||
depthValue.depthStencil.stencil = (color >> 24) & 0xFF;
|
||||
|
||||
VkClearRect rect;
|
||||
rect.baseArrayLayer = 0;
|
||||
rect.layerCount = 1;
|
||||
rect.rect.offset.x = x;
|
||||
rect.rect.offset.y = y;
|
||||
rect.rect.extent.width = w;
|
||||
rect.rect.extent.height = h;
|
||||
|
||||
int count = 0;
|
||||
VkClearAttachment attach[2];
|
||||
int mask = 0;
|
||||
// The Clear detection takes care of doing a regular draw instead if separate masking
|
||||
// of color and alpha is needed, so we can just treat them as the same.
|
||||
if (clearColor || clearAlpha) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
attach[count].clearValue = colorValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
if (clearDepth) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
attach[count].clearValue = depthValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
vkCmdClearAttachments(curCmd_, count, attach, 1, &rect);
|
||||
if (clearColor || clearAlpha)
|
||||
mask |= Draw::FBChannel::FB_COLOR_BIT;
|
||||
if (clearDepth)
|
||||
mask |= Draw::FBChannel::FB_DEPTH_BIT;
|
||||
if (clearAlpha)
|
||||
mask |= Draw::FBChannel::FB_STENCIL_BIT;
|
||||
|
||||
draw_->Clear(mask, color, depth, 0);
|
||||
if (clearColor || clearAlpha) {
|
||||
SetColorUpdated(gstate_c.skipDrawReason);
|
||||
}
|
||||
if (clearDepth) {
|
||||
SetDepthUpdated();
|
||||
}
|
||||
} else {
|
||||
//} else {
|
||||
// TODO: Clever render pass magic.
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DoNotifyDraw() {
|
||||
@ -345,6 +240,8 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
||||
drawPixelsTex_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
// Initialize backbuffer texture for DrawPixels
|
||||
drawPixelsTexFormat_ = srcPixelFormat;
|
||||
} else {
|
||||
drawPixelsTex_->TransitionForUpload();
|
||||
}
|
||||
|
||||
// TODO: We can just change the texture format and flip some bits around instead of this.
|
||||
@ -403,6 +300,8 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
||||
size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer);
|
||||
drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width);
|
||||
drawPixelsTex_->EndCreate();
|
||||
|
||||
overrideImageView_ = drawPixelsTex_->GetImageView();
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) {
|
||||
@ -413,15 +312,12 @@ void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) {
|
||||
vp.y = (float)y;
|
||||
vp.width = (float)w;
|
||||
vp.height = (float)h;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
|
||||
void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation) {
|
||||
float texCoords[8] = {
|
||||
u0,v0,
|
||||
u1,v0,
|
||||
@ -444,10 +340,10 @@ void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, floa
|
||||
}
|
||||
|
||||
Vulkan2D::Vertex vtx[4] = {
|
||||
{x,y,0,texCoords[0],texCoords[1]},
|
||||
{x + w,y,0,texCoords[2],texCoords[3]},
|
||||
{x,y + h,0,texCoords[6],texCoords[7] },
|
||||
{x + w,y + h,0,texCoords[4],texCoords[5] },
|
||||
{x, y, 0, texCoords[0], texCoords[1]},
|
||||
{x + w, y, 0, texCoords[2], texCoords[3]},
|
||||
{x, y + h, 0, texCoords[6], texCoords[7]},
|
||||
{x + w, y + h, 0, texCoords[4], texCoords[5]},
|
||||
};
|
||||
|
||||
float invDestW = 1.0f / (destW * 0.5f);
|
||||
@ -457,13 +353,18 @@ void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, floa
|
||||
vtx[i].y = vtx[i].y * invDestH - 1.0f;
|
||||
}
|
||||
|
||||
draw_->FlushState();
|
||||
|
||||
// TODO: Should probably use draw_ directly and not go low level
|
||||
|
||||
VulkanPushBuffer *push = frameData_[curFrame_].push_;
|
||||
|
||||
VkCommandBuffer cmd = curCmd_;
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
|
||||
// TODO: Choose linear or nearest appropriately, see GL impl.
|
||||
vulkan2D_.BindDescriptorSet(cmd, texture->GetImageView(), linearSampler_);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW);
|
||||
overrideImageView_ = VK_NULL_HANDLE;
|
||||
vulkan2D_.BindDescriptorSet(cmd, view, linearFilter ? linearSampler_ : nearestSampler_);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_);
|
||||
VkBuffer vbuffer;
|
||||
VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset);
|
||||
@ -471,7 +372,8 @@ void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, floa
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Bind2DShader() {
|
||||
|
||||
VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS);
|
||||
cur2DPipeline_ = vulkan2D_.GetPipeline(pipelineCache2D_, rp, vsBasicTex_, fsBasicTex_);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) {
|
||||
@ -480,9 +382,10 @@ void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms
|
||||
|
||||
void FramebufferManagerVulkan::RebindFramebuffer() {
|
||||
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo);
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
} else {
|
||||
draw_->BindBackbufferAsRenderTarget();
|
||||
// Should this even happen?
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,14 +403,12 @@ int FramebufferManagerVulkan::GetLineWidth() {
|
||||
}
|
||||
}
|
||||
|
||||
// This also binds vfb as the current render target.
|
||||
void FramebufferManagerVulkan::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
|
||||
if (!useBufferedRendering_ || !vfb->fbo) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
BindFramebufferAsRenderTargetvfb->fbo);
|
||||
|
||||
// Technically, we should at this point re-interpret the bytes of the old format to the new.
|
||||
// That might get tricky, and could cause unnecessary slowness in some games.
|
||||
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
|
||||
@ -518,30 +419,70 @@ void FramebufferManagerVulkan::ReformatFramebufferFrom(VirtualFramebuffer *vfb,
|
||||
// to exactly reproduce in 4444 and 8888 formats.
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
// TODO: Clear to black, set stencil to 0, don't touch depth (or maybe zap depth).
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
|
||||
RebindFramebuffer();
|
||||
*/
|
||||
}
|
||||
|
||||
// Except for a missing rebind and silly scissor enables, identical copy of the same function in GPU_GLES - tricky parts are in thin3d.
|
||||
void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) {
|
||||
if (src->z_address == dst->z_address &&
|
||||
src->z_stride != 0 && dst->z_stride != 0 &&
|
||||
src->renderWidth == dst->renderWidth &&
|
||||
src->renderHeight == dst->renderHeight) {
|
||||
if (g_Config.bDisableSlowFramebufEffects) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Let's only do this if not clearing depth.
|
||||
bool matchingDepthBuffer = src->z_address == dst->z_address && src->z_stride != 0 && dst->z_stride != 0;
|
||||
bool matchingSize = src->width == dst->width && src->height == dst->height;
|
||||
bool matchingRenderSize = src->renderWidth == dst->renderWidth && src->renderHeight == dst->renderHeight;
|
||||
|
||||
VkImageCopy region = {};
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
region.extent = { dst->renderWidth, dst->renderHeight, 1 };
|
||||
region.extent.depth = 1;
|
||||
// vkCmdCopyImage(curCmd_, src->fbo->GetDepthStencil()->GetImage(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
// dst->fbo->GetDepthStencil()->GetImage(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1, ®ion);
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_ANY_COPY_IMAGE) && matchingDepthBuffer && matchingRenderSize && matchingSize) {
|
||||
draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT);
|
||||
} else if (matchingDepthBuffer && matchingSize) {
|
||||
int w = std::min(src->renderWidth, dst->renderWidth);
|
||||
int h = std::min(src->renderHeight, dst->renderHeight);
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT)) {
|
||||
draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we set dst->depthUpdated here, our optimization above would be pointless.
|
||||
VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) {
|
||||
if (!framebuffer->fbo || !useBufferedRendering_) {
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
|
||||
// Let's just not bother with the copy in that case.
|
||||
bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0;
|
||||
if (GPUStepping::IsStepping() || g_Config.bDisableSlowFramebufEffects) {
|
||||
skipCopy = true;
|
||||
}
|
||||
// Currently rendering to this framebuffer. Need to make a copy.
|
||||
if (!skipCopy && framebuffer == currentRenderVfb_) {
|
||||
// ignore this case for now, doesn't work
|
||||
// ILOG("Texturing from current render Vfb!");
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
|
||||
Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth);
|
||||
if (renderCopy) {
|
||||
VirtualFramebuffer copyInfo = *framebuffer;
|
||||
copyInfo.fbo = renderCopy;
|
||||
CopyFramebufferForColorTexture(©Info, framebuffer, flags);
|
||||
RebindFramebuffer();
|
||||
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, 0);
|
||||
} else {
|
||||
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0);
|
||||
}
|
||||
return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW);
|
||||
} else if (framebuffer != currentRenderVfb_) {
|
||||
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0);
|
||||
return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW);
|
||||
} else {
|
||||
ERROR_LOG_REPORT_ONCE(vulkanSelfTexture, G3D, "Attempting to texture from target");
|
||||
// To do this safely in Vulkan, we need to use input attachments.
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,15 +662,13 @@ void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb
|
||||
void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
|
||||
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
|
||||
// This can happen if they recently switched from non-buffered.
|
||||
if (useBufferedRendering_)
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: There may be cases (like within a renderpass) where we want to
|
||||
// not use a blit.
|
||||
bool useBlit = true;
|
||||
|
||||
float srcXFactor = useBlit ? (float)src->renderWidth / (float)src->bufferWidth : 1.0f;
|
||||
float srcYFactor = useBlit ? (float)src->renderHeight / (float)src->bufferHeight : 1.0f;
|
||||
float srcXFactor = (float)src->renderWidth / (float)src->bufferWidth;
|
||||
float srcYFactor = (float)src->renderHeight / (float)src->bufferHeight;
|
||||
const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2;
|
||||
if (srcBpp != bpp && bpp != 0) {
|
||||
srcXFactor = (srcXFactor * bpp) / srcBpp;
|
||||
@ -739,8 +678,8 @@ void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
|
||||
int srcY1 = srcY * srcYFactor;
|
||||
int srcY2 = (srcY + h) * srcYFactor;
|
||||
|
||||
float dstXFactor = useBlit ? (float)dst->renderWidth / (float)dst->bufferWidth : 1.0f;
|
||||
float dstYFactor = useBlit ? (float)dst->renderHeight / (float)dst->bufferHeight : 1.0f;
|
||||
float dstXFactor = (float)dst->renderWidth / (float)dst->bufferWidth;
|
||||
float dstYFactor = (float)dst->renderHeight / (float)dst->bufferHeight;
|
||||
const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2;
|
||||
if (dstBpp != bpp && bpp != 0) {
|
||||
dstXFactor = (dstXFactor * bpp) / dstBpp;
|
||||
@ -756,6 +695,7 @@ void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
|
||||
return;
|
||||
}
|
||||
|
||||
// BlitFramebuffer can clip, but CopyFramebufferImage is more restricted.
|
||||
// In case the src goes outside, we just skip the optimization in that case.
|
||||
const bool sameSize = dstX2 - dstX1 == srcX2 - srcX1 && dstY2 - dstY1 == srcY2 - srcY1;
|
||||
const bool sameDepth = dst->colorDepth == src->colorDepth;
|
||||
@ -764,30 +704,9 @@ void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
|
||||
const bool xOverlap = src == dst && srcX2 > dstX1 && srcX1 < dstX2;
|
||||
const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2;
|
||||
if (sameSize && sameDepth && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) {
|
||||
VkImageCopy region = {};
|
||||
region.extent = { (uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1), 1 };
|
||||
/*
|
||||
glCopyImageSubDataOES(
|
||||
fbo_get_color_texture(src->fbo), GL_TEXTURE_2D, 0, srcX1, srcY1, 0,
|
||||
fbo_get_color_texture(dst->fbo), GL_TEXTURE_2D, 0, dstX1, dstY1, 0,
|
||||
dstX2 - dstX1, dstY2 - dstY1, 1);
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// BindFramebufferAsRenderTargetdst->fbo);
|
||||
|
||||
if (useBlit) {
|
||||
// fbo_bind_for_read(src->fbo);
|
||||
//glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT);
|
||||
} else {
|
||||
// fbo_bind_color_as_texture(src->fbo, 0);
|
||||
|
||||
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
|
||||
// Should maybe revamp that interface.
|
||||
float srcW = src->bufferWidth;
|
||||
float srcH = src->bufferHeight;
|
||||
// DrawActiveTexture(0, dstX1, dstY1, w * dstXFactor, h, dst->bufferWidth, dst->bufferHeight, srcX1 / srcW, srcY1 / srcH, srcX2 / srcW, srcY2 / srcH, draw2dprogram_, ROTATION_LOCKED_HORIZONTAL);
|
||||
draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1000,39 +919,26 @@ void FramebufferManagerVulkan::PackFramebufferSync_(VirtualFramebuffer *vfb, int
|
||||
|
||||
}
|
||||
|
||||
VkCommandBuffer FramebufferManagerVulkan::AllocFrameCommandBuffer() {
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
int num = frame.numCommandBuffers_;
|
||||
if (!frame.commandBuffers_[num]) {
|
||||
VkCommandBufferAllocateInfo cmd = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
cmd.commandBufferCount = 1;
|
||||
cmd.commandPool = frame.cmdPool_;
|
||||
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd, &frame.commandBuffers_[num]);
|
||||
frame.totalCommandBuffers_ = num + 1;
|
||||
}
|
||||
return frame.commandBuffers_[num];
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::BeginFrameVulkan() {
|
||||
BeginFrame();
|
||||
|
||||
vulkan2D_.BeginFrame();
|
||||
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0);
|
||||
frame.numCommandBuffers_ = 0;
|
||||
|
||||
frame.push_->Reset();
|
||||
frame.push_->Begin(vulkan_);
|
||||
|
||||
if (!useBufferedRendering_) {
|
||||
// TODO: This hackery should not be necessary. Is it? Need to check.
|
||||
// We only use a single command buffer in this case.
|
||||
curCmd_ = vulkan_->GetSurfaceCommandBuffer();
|
||||
VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer();
|
||||
VkRect2D scissor;
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ };
|
||||
vkCmdSetScissor(curCmd_, 0, 1, &scissor);
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
} else {
|
||||
// Each render pass will set up scissor again.
|
||||
}
|
||||
}
|
||||
|
||||
@ -1111,7 +1017,9 @@ void FramebufferManagerVulkan::FlushBeforeCopy() {
|
||||
// all the irrelevant state checking it'll use to decide what to do. Should
|
||||
// do something more focused here.
|
||||
SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
drawEngine_->Flush(curCmd_);
|
||||
if (!draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS))
|
||||
Crash();
|
||||
drawEngine_->Flush();
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Resized() {
|
||||
@ -1211,22 +1119,7 @@ bool FramebufferManagerVulkan::GetStencilbuffer(u32 fb_address, int fb_stride, G
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FramebufferManagerVulkan::ClearBuffer(bool keepState) {
|
||||
// keepState is irrelevant.
|
||||
if (!currentRenderVfb_) {
|
||||
return;
|
||||
}
|
||||
VkClearAttachment clear[2];
|
||||
memset(clear, 0, sizeof(clear));
|
||||
clear[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
VkClearRect rc;
|
||||
rc.baseArrayLayer = 0;
|
||||
rc.layerCount = 1;
|
||||
rc.rect.offset.x = 0;
|
||||
rc.rect.offset.y = 0;
|
||||
rc.rect.extent.width = currentRenderVfb_->bufferWidth;
|
||||
rc.rect.extent.height = currentRenderVfb_->bufferHeight;
|
||||
vkCmdClearAttachments(curCmd_, 2, clear, 1, &rc);
|
||||
// TODO: Ideally, this should never be called.
|
||||
// assert(false);
|
||||
}
|
||||
|
@ -69,9 +69,7 @@ public:
|
||||
drawEngine_ = td;
|
||||
}
|
||||
|
||||
// If texture != 0, will bind it.
|
||||
// x,y,w,h are relative to destW, destH which fill out the target completely.
|
||||
void DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation);
|
||||
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter) override;
|
||||
|
||||
void DestroyAllFBOs();
|
||||
@ -106,6 +104,7 @@ public:
|
||||
bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override;
|
||||
|
||||
virtual void RebindFramebuffer() override;
|
||||
VkImageView BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
|
||||
|
||||
// VulkanFBO *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888);
|
||||
|
||||
@ -138,7 +137,6 @@ private:
|
||||
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
|
||||
void DoNotifyDraw();
|
||||
|
||||
VkCommandBuffer AllocFrameCommandBuffer();
|
||||
void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight);
|
||||
|
||||
void PackFramebufferAsync_(VirtualFramebuffer *vfb);
|
||||
@ -149,11 +147,7 @@ private:
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
|
||||
// The command buffer of the current framebuffer pass being rendered to.
|
||||
// One framebuffer can be used as a texturing source at multiple times in a frame,
|
||||
// but then the contents have to be copied out into a new texture every time.
|
||||
VkCommandBuffer curCmd_ = VK_NULL_HANDLE;
|
||||
VkCommandBuffer cmdInit_ = VK_NULL_HANDLE;
|
||||
// Used to keep track of command buffers here but have moved all that into Thin3D.
|
||||
|
||||
// Used by DrawPixels
|
||||
VulkanTexture *drawPixelsTex_;
|
||||
@ -173,13 +167,9 @@ private:
|
||||
MAX_COMMAND_BUFFERS = 32,
|
||||
};
|
||||
|
||||
// Commandbuffers are handled internally in thin3d, one for each framebuffer pass.
|
||||
struct FrameData {
|
||||
VkCommandPool cmdPool_;
|
||||
// Keep track of command buffers we allocated so we can reset or free them at an appropriate point.
|
||||
VkCommandBuffer commandBuffers_[MAX_COMMAND_BUFFERS];
|
||||
VulkanPushBuffer *push_;
|
||||
int numCommandBuffers_;
|
||||
int totalCommandBuffers_;
|
||||
};
|
||||
|
||||
FrameData frameData_[2];
|
||||
@ -188,18 +178,13 @@ private:
|
||||
// This gets copied to the current frame's push buffer as needed.
|
||||
PostShaderUniforms postUniforms_;
|
||||
|
||||
// Renderpasses, all combination of preserving or clearing fb contents
|
||||
VkRenderPass rpLoadColorLoadDepth_;
|
||||
VkRenderPass rpClearColorLoadDepth_;
|
||||
VkRenderPass rpLoadColorClearDepth_;
|
||||
VkRenderPass rpClearColorClearDepth_;
|
||||
|
||||
VkPipelineCache pipelineCache2D_;
|
||||
|
||||
// Basic shaders
|
||||
VkShaderModule fsBasicTex_;
|
||||
VkShaderModule vsBasicTex_;
|
||||
VkPipeline pipelineBasicTex_;
|
||||
|
||||
VkPipeline cur2DPipeline_ = VK_NULL_HANDLE;
|
||||
|
||||
// Postprocessing
|
||||
VkPipeline pipelinePostShader_;
|
||||
@ -207,6 +192,9 @@ private:
|
||||
VkSampler linearSampler_;
|
||||
VkSampler nearestSampler_;
|
||||
|
||||
// hack!
|
||||
VkImageView overrideImageView_ = VK_NULL_HANDLE;
|
||||
|
||||
// Simple 2D drawing engine.
|
||||
Vulkan2D vulkan2D_;
|
||||
};
|
||||
|
@ -78,7 +78,7 @@ static const VulkanCommandTableEntry commandTable[] = {
|
||||
GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw),
|
||||
vulkan_((VulkanContext *)gfxCtx->GetAPIContext()),
|
||||
drawEngine_(vulkan_) {
|
||||
drawEngine_(vulkan_, draw) {
|
||||
UpdateVsyncInterval(true);
|
||||
CheckGPUFeatures();
|
||||
|
||||
@ -197,10 +197,6 @@ void GPU_Vulkan::CheckGPUFeatures() {
|
||||
}
|
||||
|
||||
void GPU_Vulkan::BeginHostFrame() {
|
||||
if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) {
|
||||
// Draw everything directly to the backbuffer.
|
||||
drawEngine_.SetCmdBuffer(vulkan_->GetSurfaceCommandBuffer());
|
||||
}
|
||||
drawEngine_.BeginFrame();
|
||||
|
||||
if (resized_) {
|
||||
@ -396,7 +392,7 @@ bool GPU_Vulkan::FramebufferReallyDirty() {
|
||||
|
||||
void GPU_Vulkan::CopyDisplayToOutputInternal() {
|
||||
// Flush anything left over.
|
||||
drawEngine_.Flush(curCmd_);
|
||||
drawEngine_.Flush();
|
||||
|
||||
shaderManagerVulkan_->DirtyLastShader();
|
||||
|
||||
@ -419,7 +415,7 @@ void GPU_Vulkan::FastRunLoop(DisplayList &list) {
|
||||
const u32 diff = op ^ gstate.cmdmem[cmd];
|
||||
// Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check.
|
||||
if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) {
|
||||
drawEngine_.Flush(curCmd_);
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0...
|
||||
if ((cmdFlags & FLAG_EXECUTE) || (diff && (cmdFlags & FLAG_EXECUTEONCHANGE))) {
|
||||
@ -445,7 +441,7 @@ inline void GPU_Vulkan::CheckFlushOp(int cmd, u32 diff) {
|
||||
if (dumpThisFrame_) {
|
||||
NOTICE_LOG(G3D, "================ FLUSH ================");
|
||||
}
|
||||
drawEngine_.Flush(curCmd_);
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,6 +489,8 @@ void GPU_Vulkan::Execute_Prim(u32 op, u32 diff) {
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (!draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS))
|
||||
Crash();
|
||||
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
|
@ -96,7 +96,7 @@ protected:
|
||||
|
||||
private:
|
||||
void Flush() {
|
||||
drawEngine_.Flush(nullptr);
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
void CheckFlushOp(int cmd, u32 diff);
|
||||
void BuildReportingInfo();
|
||||
|
@ -292,9 +292,13 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
|
||||
return vulkanPipeline;
|
||||
}
|
||||
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
VulkanPipelineKey key;
|
||||
if (!renderPass)
|
||||
Crash();
|
||||
|
||||
key.raster = rasterKey;
|
||||
key.renderPass = renderPass;
|
||||
key.useHWTransform = useHwTransform;
|
||||
key.vShader = vs->GetModule();
|
||||
key.fShader = fs->GetModule();
|
||||
@ -305,7 +309,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layo
|
||||
}
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
vulkan_->GetDevice(), pipelineCache_, layout, vulkan_->GetSurfaceRenderPass(),
|
||||
vulkan_->GetDevice(), pipelineCache_, layout, renderPass,
|
||||
rasterKey, vtxDec, vs, fs, useHwTransform);
|
||||
pipelines_[key] = pipeline;
|
||||
return pipeline;
|
||||
|
@ -39,13 +39,16 @@ enum class PspAttributeLocation {
|
||||
|
||||
struct VulkanPipelineKey {
|
||||
VulkanPipelineRasterStateKey raster; // prim is included here
|
||||
VkRenderPass renderPass;
|
||||
bool useHWTransform;
|
||||
const VertexDecoder *vtxDec;
|
||||
VkShaderModule vShader;
|
||||
VkShaderModule fShader;
|
||||
|
||||
// TODO: Probably better to use a hash function instead.
|
||||
bool operator < (const VulkanPipelineKey &other) const {
|
||||
if (raster < other.raster) return true; else if (other.raster < raster) return false;
|
||||
if (renderPass < other.renderPass) return true; else if (other.renderPass < renderPass) return false;
|
||||
if (useHWTransform < other.useHWTransform) return true; else if (other.useHWTransform < useHWTransform) return false;
|
||||
if (vtxDec < other.vtxDec) return true; else if (other.vtxDec < vtxDec) return false;
|
||||
if (vShader < other.vShader) return true; else if (other.vShader < vShader) return false;
|
||||
@ -82,7 +85,7 @@ public:
|
||||
PipelineManagerVulkan(VulkanContext *ctx);
|
||||
~PipelineManagerVulkan();
|
||||
|
||||
VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
|
||||
VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
|
||||
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
||||
|
||||
void Clear();
|
||||
|
@ -16,7 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SHADERLOG
|
||||
//#define SHADERLOG
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
@ -152,6 +152,9 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
|
||||
key.colorWriteMask = (colorMask ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) | (alphaMask ? VK_COLOR_COMPONENT_A_BIT : 0);
|
||||
|
||||
} else {
|
||||
key.logicOpEnable = false;
|
||||
key.logicOp = VK_LOGIC_OP_CLEAR;
|
||||
|
||||
// Set blend - unless we need to do it in the shader.
|
||||
GenericBlendState blendState;
|
||||
ConvertBlendState(blendState, gstate_c.allowShaderBlend);
|
||||
@ -341,10 +344,17 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
|
||||
}
|
||||
|
||||
VkRect2D &scissor = dynState.scissor;
|
||||
scissor.offset.x = vpAndScissor.scissorX;
|
||||
scissor.offset.y = vpAndScissor.scissorY;
|
||||
scissor.extent.width = vpAndScissor.scissorW;
|
||||
scissor.extent.height = vpAndScissor.scissorH;
|
||||
if (vpAndScissor.scissorEnable) {
|
||||
scissor.offset.x = vpAndScissor.scissorX;
|
||||
scissor.offset.y = vpAndScissor.scissorY;
|
||||
scissor.extent.width = vpAndScissor.scissorW;
|
||||
scissor.extent.height = vpAndScissor.scissorH;
|
||||
} else {
|
||||
scissor.offset.x = 0;
|
||||
scissor.offset.y = 0;
|
||||
scissor.extent.width = framebufferManager_->GetRenderWidth();
|
||||
scissor.extent.height = framebufferManager_->GetRenderHeight();
|
||||
}
|
||||
|
||||
float depthMin = vpAndScissor.depthRangeMin;
|
||||
float depthMax = vpAndScissor.depthRangeMax;
|
||||
@ -358,3 +368,21 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
|
||||
|
||||
key.topology = primToVulkan[prim];
|
||||
}
|
||||
|
||||
|
||||
void DrawEngineVulkan::ApplyDrawStateLate() {
|
||||
// At this point, we know if the vertices are full alpha or not.
|
||||
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
|
||||
if (!gstate.isModeClear()) {
|
||||
// TODO: Test texture?
|
||||
/*
|
||||
if (fboTexNeedBind_) {
|
||||
// Note that this is positions, not UVs, that we need the copy from.
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
|
||||
// If we are rendering at a higher resolution, linear is probably best for the dest color.
|
||||
fboTexBound_ = true;
|
||||
fboTexNeedBind_ = false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -426,30 +426,21 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr
|
||||
TexCacheEntry::Status alphaStatus = CheckAlpha(clutBuf_, getClutDestFormatVulkan(clutFormat), clutTotalColors, clutTotalColors, 1);
|
||||
gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL);
|
||||
gstate_c.SetTextureSimpleAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE);
|
||||
|
||||
// imageView_ = depalFbo->getImageView().
|
||||
} else {
|
||||
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
|
||||
imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
|
||||
|
||||
gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650);
|
||||
gstate_c.SetTextureSimpleAlpha(gstate_c.textureFullAlpha);
|
||||
}
|
||||
|
||||
/*
|
||||
imageView = depalFBO->GetColorImageView();
|
||||
|
||||
SamplerCacheKey samplerKey;
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey);
|
||||
sampler = GetOrCreateSampler(samplerKey);
|
||||
*/
|
||||
|
||||
if (entry->vkTex) {
|
||||
SamplerCacheKey key;
|
||||
UpdateSamplingParams(*entry, key);
|
||||
key.mipEnable = false;
|
||||
sampler_ = samplerCache_.GetOrCreateSampler(key);
|
||||
|
||||
InvalidateLastTexture();
|
||||
}
|
||||
sampler_ = samplerCache_.GetOrCreateSampler(samplerKey);
|
||||
InvalidateLastTexture(entry);
|
||||
}
|
||||
|
||||
ReplacedTextureFormat FromVulkanFormat(VkFormat fmt) {
|
||||
@ -622,7 +613,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
||||
entry->vkTex = nullptr;
|
||||
}
|
||||
} else {
|
||||
// TODO: If reusing an existing texture object, we must transition it into the correct layout.
|
||||
entry->vkTex->texture_->TransitionForUpload();
|
||||
}
|
||||
lastBoundTexture = entry->vkTex;
|
||||
|
||||
@ -660,8 +651,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt);
|
||||
entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
break;
|
||||
} else
|
||||
} else {
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt);
|
||||
}
|
||||
if (replacer_.Enabled()) {
|
||||
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight);
|
||||
}
|
||||
|
@ -217,6 +217,7 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade
|
||||
PipelineKey key;
|
||||
key.vs = vs;
|
||||
key.fs = fs;
|
||||
key.rp = rp;
|
||||
|
||||
auto iter = pipelines_.find(key);
|
||||
if (iter != pipelines_.end()) {
|
||||
|
@ -169,16 +169,6 @@ void EmuScreen::bootGame(const std::string &filename) {
|
||||
break;
|
||||
case GPUBackend::VULKAN:
|
||||
coreParam.gpuCore = GPUCORE_VULKAN;
|
||||
if (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE && !g_Config.bSoftwareRendering) {
|
||||
#ifdef _WIN32
|
||||
if (IDYES == MessageBox(MainWindow::GetHWND(), L"The Vulkan backend is not yet compatible with buffered rendering. Switch to non-buffered (WARNING: This will cause glitches with the other backends unless you switch back)", L"Vulkan Experimental Support", MB_ICONINFORMATION | MB_YESNO)) {
|
||||
g_Config.iRenderingMode = FB_NON_BUFFERED_MODE;
|
||||
} else {
|
||||
errorMessage_ = "Non-buffered rendering required for Vulkan";
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@ -958,6 +948,42 @@ static void DrawFPS(DrawBuffer *draw2d, const Bounds &bounds) {
|
||||
draw2d->SetFontScale(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void EmuScreen::preRender() {
|
||||
using namespace Draw;
|
||||
DrawContext *draw = screenManager()->getDrawContext();
|
||||
draw->BeginFrame();
|
||||
// Here we do NOT bind the backbuffer or clear the screen, unless non-buffered.
|
||||
// The emuscreen is different than the others - we really want to allow the game to render to framebuffers
|
||||
// before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render
|
||||
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
|
||||
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
|
||||
// We do, however, start the frame in other ways.
|
||||
|
||||
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
|
||||
if (!useBufferedRendering && !g_Config.bSoftwareRendering) {
|
||||
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
||||
DrawContext *draw = screenManager()->getDrawContext();
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 });
|
||||
|
||||
Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
viewport.TopLeftY = 0;
|
||||
viewport.Width = pixel_xres;
|
||||
viewport.Height = pixel_yres;
|
||||
viewport.MaxDepth = 1.0;
|
||||
viewport.MinDepth = 0.0;
|
||||
draw->SetViewports(1, &viewport);
|
||||
draw->SetTargetSize(pixel_xres, pixel_yres);
|
||||
}
|
||||
}
|
||||
|
||||
void EmuScreen::postRender() {
|
||||
Draw::DrawContext *draw = screenManager()->getDrawContext();
|
||||
if (!draw)
|
||||
return;
|
||||
draw->EndFrame();
|
||||
}
|
||||
|
||||
void EmuScreen::render() {
|
||||
using namespace Draw;
|
||||
|
||||
@ -979,23 +1005,6 @@ void EmuScreen::render() {
|
||||
}
|
||||
}
|
||||
|
||||
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
|
||||
|
||||
if (!useBufferedRendering) {
|
||||
DrawContext *draw = screenManager()->getDrawContext();
|
||||
draw->Clear(FBChannel::FB_COLOR_BIT | FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT, 0xFF000000, 0.0f, 0);
|
||||
|
||||
Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
viewport.TopLeftY = 0;
|
||||
viewport.Width = pixel_xres;
|
||||
viewport.Height = pixel_yres;
|
||||
viewport.MaxDepth = 1.0;
|
||||
viewport.MinDepth = 0.0;
|
||||
draw->SetViewports(1, &viewport);
|
||||
draw->SetTargetSize(pixel_xres, pixel_yres);
|
||||
}
|
||||
|
||||
PSP_BeginHostFrame();
|
||||
|
||||
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
|
||||
@ -1018,6 +1027,8 @@ void EmuScreen::render() {
|
||||
|
||||
if (invalid_)
|
||||
return;
|
||||
|
||||
// Here the backbuffer will always be bound.
|
||||
|
||||
if (!osm.IsEmpty() || g_Config.bShowDebugStats || g_Config.iShowFPSCounter || g_Config.bShowTouchControls || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowFrameProfiler) {
|
||||
DrawContext *thin3d = screenManager()->getDrawContext();
|
||||
|
@ -37,6 +37,8 @@ public:
|
||||
|
||||
void update() override;
|
||||
void render() override;
|
||||
void preRender() override;
|
||||
void postRender() override;
|
||||
void deviceLost() override;
|
||||
void deviceRestore() override;
|
||||
void dialogFinished(const Screen *dialog, DialogResult result) override;
|
||||
|
@ -720,6 +720,7 @@ std::shared_ptr<GameInfo> GameInfoCache::GetInfo(Draw::DrawContext *draw, const
|
||||
if (info->IsWorking()) {
|
||||
// Uh oh, it's currently in process. It could mark pending = false with the wrong wantFlags.
|
||||
// Let's wait it out, then queue.
|
||||
// NOTE: This is bad because we're likely on the UI thread....
|
||||
WaitUntilDone(info);
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,7 @@ void GameSettingsScreen::CreateViews() {
|
||||
renderingBackendChoice->HideChoice(2); // D3D11
|
||||
}
|
||||
#endif
|
||||
#if !defined(_WIN32)
|
||||
#if !defined(_WIN32) && !PPSSPP_PLATFORM(ANDROID)
|
||||
// TODO: Add dynamic runtime check for Vulkan support on Android
|
||||
renderingBackendChoice->HideChoice(3);
|
||||
#endif
|
||||
|
@ -510,7 +510,7 @@ void LogoScreen::render() {
|
||||
dc.DrawTextShadow(boot_filename.c_str(), bounds.centerX(), bounds.centerY() + 180, textColor, ALIGN_CENTER);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
|
||||
#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(ANDROID)
|
||||
// Draw the graphics API, except on UWP where it's always D3D11
|
||||
dc.DrawText(screenManager()->getDrawContext()->GetInfoString(InfoField::APINAME).c_str(), bounds.centerX(), bounds.y2() - 100, textColor, ALIGN_CENTER);
|
||||
#endif
|
||||
|
@ -588,6 +588,8 @@ static void UIThemeInit() {
|
||||
ui_theme.popupStyle = MakeStyle(g_Config.uPopupStyleFg, g_Config.uPopupStyleBg);
|
||||
}
|
||||
|
||||
void RenderOverlays(UIContext *dc, void *userdata);
|
||||
|
||||
void NativeInitGraphics(GraphicsContext *graphicsContext) {
|
||||
ILOG("NativeInitGraphics");
|
||||
|
||||
@ -652,6 +654,7 @@ void NativeInitGraphics(GraphicsContext *graphicsContext) {
|
||||
|
||||
screenManager->setUIContext(uiContext);
|
||||
screenManager->setDrawContext(g_draw);
|
||||
screenManager->setPostRenderCallback(&RenderOverlays, nullptr);
|
||||
|
||||
UIBackgroundInit(*uiContext);
|
||||
|
||||
@ -737,7 +740,7 @@ void TakeScreenshot() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawDownloadsOverlay(UIContext &dc) {
|
||||
void RenderOverlays(UIContext *dc, void *userdata) {
|
||||
// Thin bar at the top of the screen like Chrome.
|
||||
std::vector<float> progress = g_DownloadManager.GetCurrentProgress();
|
||||
if (progress.empty()) {
|
||||
@ -751,27 +754,34 @@ void DrawDownloadsOverlay(UIContext &dc) {
|
||||
0xFF777777,
|
||||
};
|
||||
|
||||
dc.Begin();
|
||||
dc->Begin();
|
||||
int h = 5;
|
||||
for (size_t i = 0; i < progress.size(); i++) {
|
||||
float barWidth = 10 + (dc.GetBounds().w - 10) * progress[i];
|
||||
float barWidth = 10 + (dc->GetBounds().w - 10) * progress[i];
|
||||
Bounds bounds(0, h * i, barWidth, h);
|
||||
UI::Drawable solid(colors[i & 3]);
|
||||
dc.FillRect(solid, bounds);
|
||||
dc->FillRect(solid, bounds);
|
||||
}
|
||||
dc->End();
|
||||
dc->Flush();
|
||||
|
||||
if (g_TakeScreenshot) {
|
||||
TakeScreenshot();
|
||||
}
|
||||
dc.End();
|
||||
dc.Flush();
|
||||
}
|
||||
|
||||
void NativeRender(GraphicsContext *graphicsContext) {
|
||||
g_GameManager.Update();
|
||||
|
||||
// If uitexture gets reloaded, make sure we use the latest one.
|
||||
// Not sure this happens anymore now that we tear down all graphics on app switches...
|
||||
uiContext->FrameSetup(uiTexture->GetTexture());
|
||||
|
||||
float xres = dp_xres;
|
||||
float yres = dp_yres;
|
||||
|
||||
// Apply the UIContext bounds as a 2D transformation matrix.
|
||||
// TODO: This should be moved into the draw context...
|
||||
Matrix4x4 ortho;
|
||||
switch (GetGPUBackend()) {
|
||||
case GPUBackend::VULKAN:
|
||||
@ -798,19 +808,12 @@ void NativeRender(GraphicsContext *graphicsContext) {
|
||||
ui_draw2d.PushDrawMatrix(ortho);
|
||||
ui_draw2d_front.PushDrawMatrix(ortho);
|
||||
|
||||
// All actual rendering happen in here.
|
||||
screenManager->render();
|
||||
if (screenManager->getUIContext()->Text()) {
|
||||
screenManager->getUIContext()->Text()->OncePerFrame();
|
||||
}
|
||||
|
||||
// At this point, the vulkan context has been "ended" already, no more drawing can be done in this frame.
|
||||
// TODO: Integrate the download overlay with the screen system
|
||||
DrawDownloadsOverlay(*screenManager->getUIContext());
|
||||
|
||||
if (g_TakeScreenshot) {
|
||||
TakeScreenshot();
|
||||
}
|
||||
|
||||
if (resized) {
|
||||
resized = false;
|
||||
|
||||
|
@ -124,11 +124,18 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep
|
||||
}
|
||||
message << "[" << pLayerPrefix << "] " << ObjTypeToString(objType) << " Code " << msgCode << " : " << pMsg << "\n";
|
||||
|
||||
if (msgCode == 2) // Useless perf warning
|
||||
return false;
|
||||
|
||||
// This seems like a bogus result when submitting two command buffers in one go, one creating the image, the other one using it.
|
||||
if (msgCode == 6 && startsWith(pMsg, "Cannot submit cmd buffer using image"))
|
||||
return false;
|
||||
if (msgCode == 11)
|
||||
return false;
|
||||
// Silence "invalid reads of buffer data" - usually just uninitialized color buffers that will immediately get cleared due to our
|
||||
// lacking clearing optimizations.
|
||||
if (msgCode == 15 && objType == VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT)
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string msg = message.str();
|
||||
@ -141,7 +148,7 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep
|
||||
MessageBoxA(NULL, message.str().c_str(), "Alert", MB_OK);
|
||||
}
|
||||
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
|
||||
if (options->breakOnWarning) {
|
||||
if (options->breakOnWarning && IsDebuggerPresent()) {
|
||||
DebugBreak();
|
||||
}
|
||||
}
|
||||
|
@ -321,6 +321,10 @@ enum class NativeObject {
|
||||
BACKBUFFER_COLOR_TEX,
|
||||
BACKBUFFER_DEPTH_TEX,
|
||||
FEATURE_LEVEL,
|
||||
COMPATIBLE_RENDERPASS,
|
||||
CURRENT_RENDERPASS,
|
||||
RENDERPASS_COMMANDBUFFER,
|
||||
BOUND_TEXTURE_IMAGEVIEW,
|
||||
};
|
||||
|
||||
enum FBColorDepth {
|
||||
@ -562,6 +566,20 @@ struct TextureDesc {
|
||||
std::vector<uint8_t *> initData;
|
||||
};
|
||||
|
||||
enum class RPAction {
|
||||
DONT_CARE,
|
||||
CLEAR,
|
||||
KEEP,
|
||||
};
|
||||
|
||||
struct RenderPassInfo {
|
||||
RPAction color;
|
||||
RPAction depth;
|
||||
uint32_t clearColor;
|
||||
float clearDepth;
|
||||
uint8_t clearStencil;
|
||||
};
|
||||
|
||||
class DrawContext {
|
||||
public:
|
||||
virtual ~DrawContext();
|
||||
@ -600,16 +618,20 @@ public:
|
||||
virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) = 0;
|
||||
|
||||
// These functions should be self explanatory.
|
||||
virtual void BindFramebufferAsRenderTarget(Framebuffer *fbo) = 0;
|
||||
// Binding a zero render target means binding the backbuffer.
|
||||
virtual void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) = 0;
|
||||
|
||||
// color must be 0, for now.
|
||||
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0;
|
||||
virtual void BindFramebufferForRead(Framebuffer *fbo) = 0;
|
||||
|
||||
virtual void BindBackbufferAsRenderTarget() = 0;
|
||||
virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) = 0;
|
||||
|
||||
virtual void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) = 0;
|
||||
|
||||
// Useful in OpenGL ES to give hints about framebuffers on tiler GPUs.
|
||||
virtual void InvalidateFramebuffer(Framebuffer *fbo) {}
|
||||
|
||||
// Dynamic state
|
||||
virtual void SetScissorRect(int left, int top, int width, int height) = 0;
|
||||
virtual void SetViewports(int count, Viewport *viewports) = 0;
|
||||
@ -636,13 +658,14 @@ public:
|
||||
virtual void DrawIndexed(int vertexCount, int offset) = 0;
|
||||
virtual void DrawUP(const void *vdata, int vertexCount) = 0;
|
||||
|
||||
// Render pass management. Default implementations here.
|
||||
virtual void Begin(bool clear, uint32_t colorval, float depthVal, int stencilVal) {
|
||||
Clear(0xF, colorval, depthVal, stencilVal);
|
||||
}
|
||||
virtual void End() {}
|
||||
// Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here.
|
||||
virtual void BeginFrame() {}
|
||||
virtual void EndFrame() {}
|
||||
|
||||
// This should be avoided as much as possible, in favor of clearing when binding a render target, which is native
|
||||
// on Vulkan.
|
||||
virtual void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) = 0;
|
||||
|
||||
|
||||
// Necessary to correctly flip scissor rectangles etc for OpenGL.
|
||||
void SetTargetSize(int w, int h) {
|
||||
targetWidth_ = w;
|
||||
@ -654,6 +677,16 @@ public:
|
||||
|
||||
virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0;
|
||||
|
||||
// This flushes command buffers and waits for execution at the point of the end of the last
|
||||
// renderpass that wrote to the requested framebuffer. This is needed before trying to read it back
|
||||
// on modern APIs like Vulkan. Ifr the framebuffer is currently being rendered to, we'll just end the render pass.
|
||||
// The next draw call will automatically start up a new one.
|
||||
// APIs like OpenGL won't need to implement this one.
|
||||
virtual void WaitRenderCompletion(Framebuffer *fbo) {}
|
||||
|
||||
// Flush state like scissors etc so the caller can do its own custom drawing.
|
||||
virtual void FlushState() {}
|
||||
|
||||
protected:
|
||||
void CreatePresets();
|
||||
|
||||
|
@ -58,12 +58,11 @@ public:
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override;
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo) override;
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override;
|
||||
|
||||
void BindBackbufferAsRenderTarget() override;
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
@ -1287,18 +1286,36 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
|
||||
return false;
|
||||
}
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) {
|
||||
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
|
||||
// TODO: deviceContext1 can actually discard. Useful on Windows Mobile.
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
if (curRenderTargetView_ == fb->colorRTView && curDepthStencilView_ == fb->depthStencilRTView) {
|
||||
return;
|
||||
if (fbo) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
if (curRenderTargetView_ == fb->colorRTView && curDepthStencilView_ == fb->depthStencilRTView) {
|
||||
return;
|
||||
}
|
||||
context_->OMSetRenderTargets(1, &fb->colorRTView, fb->depthStencilRTView);
|
||||
curRenderTargetView_ = fb->colorRTView;
|
||||
curDepthStencilView_ = fb->depthStencilRTView;
|
||||
curRTWidth_ = fb->width;
|
||||
curRTHeight_ = fb->height;
|
||||
} else {
|
||||
if (curRenderTargetView_ == bbRenderTargetView_ && curDepthStencilView_ == bbDepthStencilView_)
|
||||
return;
|
||||
context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_);
|
||||
curRenderTargetView_ = bbRenderTargetView_;
|
||||
curDepthStencilView_ = bbDepthStencilView_;
|
||||
curRTWidth_ = bbWidth_;
|
||||
curRTHeight_ = bbHeight_;
|
||||
}
|
||||
if (rp.color == RPAction::CLEAR && curRenderTargetView_) {
|
||||
float cv[4]{};
|
||||
if (rp.clearColor)
|
||||
Uint8x4ToFloat4(cv, rp.clearColor);
|
||||
context_->ClearRenderTargetView(curRenderTargetView_, cv);
|
||||
}
|
||||
if (rp.depth == RPAction::CLEAR && curDepthStencilView_) {
|
||||
context_->ClearDepthStencilView(curDepthStencilView_, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, rp.clearDepth, rp.clearStencil);
|
||||
}
|
||||
context_->OMSetRenderTargets(1, &fb->colorRTView, fb->depthStencilRTView);
|
||||
curRenderTargetView_ = fb->colorRTView;
|
||||
curDepthStencilView_ = fb->depthStencilRTView;
|
||||
curRTWidth_ = fb->width;
|
||||
curRTHeight_ = fb->height;
|
||||
}
|
||||
|
||||
// color must be 0, for now.
|
||||
@ -1311,16 +1328,6 @@ void D3D11DrawContext::BindFramebufferForRead(Framebuffer *fbo) {
|
||||
// This is meaningless in D3D11
|
||||
}
|
||||
|
||||
void D3D11DrawContext::BindBackbufferAsRenderTarget() {
|
||||
if (curRenderTargetView_ == bbRenderTargetView_ && curDepthStencilView_ == bbDepthStencilView_)
|
||||
return;
|
||||
context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_);
|
||||
curRenderTargetView_ = bbRenderTargetView_;
|
||||
curDepthStencilView_ = bbDepthStencilView_;
|
||||
curRTWidth_ = bbWidth_;
|
||||
curRTHeight_ = bbHeight_;
|
||||
}
|
||||
|
||||
uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
switch (channelBit) {
|
||||
|
@ -487,12 +487,11 @@ public:
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override;
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo) override;
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override {}
|
||||
|
||||
void BindBackbufferAsRenderTarget() override;
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
@ -1044,21 +1043,30 @@ D3D9Framebuffer::~D3D9Framebuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
void D3D9Context::BindBackbufferAsRenderTarget() {
|
||||
void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
|
||||
using namespace DX9;
|
||||
if (fbo) {
|
||||
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
|
||||
device_->SetRenderTarget(0, fb->surf);
|
||||
device_->SetDepthStencilSurface(fb->depthstencil);
|
||||
} else {
|
||||
device_->SetRenderTarget(0, deviceRTsurf);
|
||||
device_->SetDepthStencilSurface(deviceDSsurf);
|
||||
}
|
||||
|
||||
device_->SetRenderTarget(0, deviceRTsurf);
|
||||
device_->SetDepthStencilSurface(deviceDSsurf);
|
||||
dxstate.scissorRect.restore();
|
||||
dxstate.viewport.restore();
|
||||
}
|
||||
int clearFlags = 0;
|
||||
if (rp.color == RPAction::CLEAR) {
|
||||
clearFlags |= D3DCLEAR_TARGET;
|
||||
}
|
||||
if (rp.depth == RPAction::CLEAR) {
|
||||
clearFlags |= D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL;
|
||||
}
|
||||
if (clearFlags) {
|
||||
dxstate.scissorTest.force(false);
|
||||
device_->Clear(0, nullptr, clearFlags, (D3DCOLOR)SwapRB(rp.clearColor), rp.clearDepth, rp.clearStencil);
|
||||
dxstate.scissorRect.restore();
|
||||
}
|
||||
|
||||
void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo) {
|
||||
using namespace DX9;
|
||||
|
||||
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
|
||||
device_->SetRenderTarget(0, fb->surf);
|
||||
device_->SetDepthStencilSurface(fb->depthstencil);
|
||||
dxstate.scissorRect.restore();
|
||||
dxstate.viewport.restore();
|
||||
}
|
||||
|
@ -465,12 +465,11 @@ public:
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override;
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo) override;
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override;
|
||||
|
||||
void BindBackbufferAsRenderTarget() override;
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
@ -1469,6 +1468,7 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
|
||||
FLOG("Other framebuffer error: %i", status);
|
||||
break;
|
||||
}
|
||||
|
||||
// Unbind state we don't need
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
@ -1534,20 +1534,49 @@ void OpenGLContext::fbo_unbind() {
|
||||
currentReadHandle_ = 0;
|
||||
}
|
||||
|
||||
void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) {
|
||||
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
||||
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
|
||||
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
|
||||
fbo_bind_fb_target(false, fb->handle);
|
||||
// Always restore viewport after render target binding
|
||||
// TODO: Should we set viewports this way too?
|
||||
glstate.viewport.restore();
|
||||
void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void OpenGLContext::BindBackbufferAsRenderTarget() {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
fbo_unbind();
|
||||
if (fbo) {
|
||||
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
||||
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
|
||||
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
|
||||
fbo_bind_fb_target(false, fb->handle);
|
||||
// Always restore viewport after render target binding. Works around driver bugs.
|
||||
glstate.viewport.restore();
|
||||
} else {
|
||||
fbo_unbind();
|
||||
}
|
||||
int clearFlags = 0;
|
||||
if (rp.color == RPAction::CLEAR) {
|
||||
float fc[4]{};
|
||||
if (rp.clearColor) {
|
||||
Uint8x4ToFloat4(fc, rp.clearColor);
|
||||
}
|
||||
glClearColor(fc[0], fc[1], fc[2], fc[3]);
|
||||
clearFlags |= GL_COLOR_BUFFER_BIT;
|
||||
glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
}
|
||||
if (rp.depth == RPAction::CLEAR) {
|
||||
glClearDepthf(rp.clearDepth);
|
||||
glClearStencil(rp.clearStencil);
|
||||
clearFlags |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
glstate.depthWrite.force(GL_TRUE);
|
||||
glstate.stencilFunc.force(GL_ALWAYS, 0, 0);
|
||||
glstate.stencilMask.force(0xFF);
|
||||
}
|
||||
if (clearFlags) {
|
||||
glstate.scissorTest.force(false);
|
||||
glClear(clearFlags);
|
||||
glstate.scissorTest.restore();
|
||||
}
|
||||
if (rp.color == RPAction::CLEAR) {
|
||||
glstate.colorMask.restore();
|
||||
}
|
||||
if (rp.depth == RPAction::CLEAR) {
|
||||
glstate.depthWrite.restore();
|
||||
glstate.stencilFunc.restore();
|
||||
glstate.stencilMask.restore();
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
@ -1609,8 +1638,10 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in
|
||||
bits |= GL_DEPTH_BUFFER_BIT;
|
||||
if (channels & FB_STENCIL_BIT)
|
||||
bits |= GL_STENCIL_BUFFER_BIT;
|
||||
BindFramebufferAsRenderTarget(dst);
|
||||
BindFramebufferForRead(src);
|
||||
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
|
||||
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
|
||||
fbo_bind_fb_target(false, dst->handle);
|
||||
fbo_bind_fb_target(true, src->handle);
|
||||
if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) {
|
||||
glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -125,12 +125,16 @@ void ScreenManager::render() {
|
||||
backback.screen->preRender();
|
||||
backback.screen->render();
|
||||
stack_.back().screen->render();
|
||||
if (postRenderCb_)
|
||||
postRenderCb_(getUIContext(), postRenderUserdata_);
|
||||
backback.screen->postRender();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
stack_.back().screen->preRender();
|
||||
stack_.back().screen->render();
|
||||
if (postRenderCb_)
|
||||
postRenderCb_(getUIContext(), postRenderUserdata_);
|
||||
stack_.back().screen->postRender();
|
||||
break;
|
||||
}
|
||||
|
@ -92,6 +92,8 @@ enum {
|
||||
LAYER_TRANSPARENT = 2,
|
||||
};
|
||||
|
||||
typedef void(*PostRenderCallback)(UIContext *ui, void *userdata);
|
||||
|
||||
class ScreenManager {
|
||||
public:
|
||||
ScreenManager();
|
||||
@ -106,6 +108,11 @@ public:
|
||||
void setDrawContext(Draw::DrawContext *context) { thin3DContext_ = context; }
|
||||
Draw::DrawContext *getDrawContext() { return thin3DContext_; }
|
||||
|
||||
void setPostRenderCallback(PostRenderCallback cb, void *userdata) {
|
||||
postRenderCb_ = cb;
|
||||
postRenderUserdata_ = userdata;
|
||||
}
|
||||
|
||||
void render();
|
||||
void resized();
|
||||
void deviceLost();
|
||||
@ -142,6 +149,9 @@ private:
|
||||
UIContext *uiContext_;
|
||||
Draw::DrawContext *thin3DContext_;
|
||||
|
||||
PostRenderCallback postRenderCb_ = nullptr;
|
||||
void *postRenderUserdata_ = nullptr;
|
||||
|
||||
const Screen *dialogFinished_;
|
||||
DialogResult dialogResult_;
|
||||
|
||||
|
@ -61,11 +61,14 @@ void UIScreen::update() {
|
||||
}
|
||||
|
||||
void UIScreen::preRender() {
|
||||
using namespace Draw;
|
||||
Draw::DrawContext *draw = screenManager()->getDrawContext();
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
draw->Begin(true, 0xFF000000, 0.0f, 0);
|
||||
draw->BeginFrame();
|
||||
// Bind and clear the back buffer
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 });
|
||||
|
||||
Draw::Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
@ -83,7 +86,7 @@ void UIScreen::postRender() {
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
draw->End();
|
||||
draw->EndFrame();
|
||||
}
|
||||
|
||||
void UIScreen::render() {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e18cface3db64ccb96738dc128fe769b28fff65c
|
||||
Subproject commit d02ba7407050f445edf9e908374ad4bf3b2f237b
|
Loading…
x
Reference in New Issue
Block a user