diff --git a/.editorconfig b/.editorconfig index 607bc73842..21b678c82b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,7 +23,7 @@ indent_style = space indent_size = 4 [Core/Dialog/PSPOskDialog.cpp] -charset = utf-16le +charset = utf-8-bom [ext/native/tools/kanjifilter.h] # This file may need to force a BOM so compilers treat as utf-8. diff --git a/.gitignore b/.gitignore index 5b59310999..10ecce6bab 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ PPSSPPDebug64.exe.manifest PPSSPPDebug.exe.manifest assets/flash0 UWP/icph +UWP/.vs # For Mac .DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 558517394e..4e8992b23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -742,7 +742,10 @@ elseif(TARGET SDL2::SDL2) set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm) set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY}) elseif(USING_EGL) - set(nativeExtraLibs ${nativeExtraLibs} pthread EGL) + if(NOT EGL_LIBRARIES) + set(EGL_LIBRARIES EGL) + endif() + set(nativeExtraLibs ${nativeExtraLibs} pthread ${EGL_LIBRARIES}) endif() set(TargetBin PPSSPPSDL) elseif(WIN32) @@ -800,7 +803,13 @@ endif() set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp) if(VULKAN) - set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp) + set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} + ext/native/thin3d/thin3d_vulkan.cpp + ext/native/thin3d/VulkanRenderManager.cpp + ext/native/thin3d/VulkanRenderManager.h + ext/native/thin3d/VulkanQueueRunner.cpp + ext/native/thin3d/VulkanQueueRunner.h + ext/native/thin3d/DataFormat.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} @@ -1165,6 +1174,7 @@ set(GPU_VULKAN GPU/Vulkan/ShaderManagerVulkan.h GPU/Vulkan/StateMappingVulkan.cpp GPU/Vulkan/StateMappingVulkan.h + GPU/Vulkan/StencilBufferVulkan.cpp GPU/Vulkan/TextureCacheVulkan.cpp GPU/Vulkan/TextureCacheVulkan.h GPU/Vulkan/TextureScalerVulkan.cpp @@ -1257,6 +1267,8 @@ set(GPU_SOURCES GPU/Common/ShaderCommon.h GPU/Common/SplineCommon.cpp GPU/Common/SplineCommon.h + GPU/Common/StencilCommon.cpp + GPU/Common/StencilCommon.h GPU/Common/SoftwareTransformCommon.cpp GPU/Common/SoftwareTransformCommon.h GPU/Common/VertexDecoderCommon.cpp @@ -1614,7 +1626,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CoreExtraLibs -Wl,--gc-sections -Wl,--exclude-libs,ALL) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic") endif() endif() diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index d899aaf1fd..49d9ce8feb 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -136,6 +136,7 @@ void ConsoleListener::Open() // Set the console window title SetConsoleTitle(title_.c_str()); SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); // Set letter space LetterSpace(openWidth_, LOG_MAX_DISPLAY_LINES); @@ -503,7 +504,7 @@ void ConsoleListener::WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Tex SetConsoleTextAttribute(hConsole, Color); int wlen = MultiByteToWideChar(CP_UTF8, 0, Text, (int)Len, NULL, NULL); MultiByteToWideChar(CP_UTF8, 0, Text, (int)Len, tempBuf, wlen); - WriteConsole(hConsole, tempBuf, (DWORD)Len, &cCharsWritten, NULL); + WriteConsole(hConsole, tempBuf, (DWORD)wlen, &cCharsWritten, NULL); } #endif diff --git a/Common/Hashmaps.h b/Common/Hashmaps.h index c8ec27bb09..f525553f7e 100644 --- a/Common/Hashmaps.h +++ b/Common/Hashmaps.h @@ -20,7 +20,7 @@ inline bool KeyEquals(const K &a, const K &b) { return !memcmp(&a, &b, sizeof(K)); } -enum class BucketState { +enum class BucketState : uint8_t { FREE, TAKEN, REMOVED, // for linear probing to work (and removal during deletion) we need tombstones @@ -35,6 +35,7 @@ class DenseHashMap { public: DenseHashMap(int initialCapacity) : capacity_(initialCapacity) { map.resize(initialCapacity); + state.resize(initialCapacity); } // Returns nullptr if no entry was found. @@ -44,9 +45,9 @@ public: // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN && KeyEquals(key, map[p].key)) + if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) return map[p].value; - else if (map[p].state == BucketState::FREE) + else if (state[p] == BucketState::FREE) return NullValue; p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) @@ -65,7 +66,7 @@ public: uint32_t pos = HashKey(key) & mask; uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN) { + if (state[p] == BucketState::TAKEN) { if (KeyEquals(key, map[p].key)) { Crash(); // Bad! We already got this one. Let's avoid this case. return false; @@ -81,10 +82,10 @@ public: Crash(); } } - if (map[p].state == BucketState::REMOVED) { + if (state[p] == BucketState::REMOVED) { removedCount_--; } - map[p].state = BucketState::TAKEN; + state[p] = BucketState::TAKEN; map[p].key = key; map[p].value = value; count_++; @@ -95,10 +96,10 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = HashKey(key) & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN && KeyEquals(key, map[p].key)) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) { // Got it! Mark it as removed. - map[p].state = BucketState::REMOVED; + state[p] = BucketState::REMOVED; removedCount_++; count_--; return true; @@ -118,17 +119,15 @@ public: template inline void Iterate(T func) { - for (auto &iter : map) { - if (iter.state == BucketState::TAKEN) { - func(iter.key, iter.value); + for (size_t i = 0; i < map.size(); i++) { + if (state[i] == BucketState::TAKEN) { + func(map[i].key, map[i].value); } } } void Clear() { - // TODO: Speedup? - map.clear(); - map.resize(capacity_); + memset(state.data(), (int)BucketState::FREE, state.size()); } void Rebuild() { @@ -147,23 +146,24 @@ private: // We simply move out the existing data, then we re-insert the old. // This is extremely non-atomic and will need synchronization. std::vector old = std::move(map); + std::vector oldState = std::move(state); capacity_ *= factor; - map.clear(); map.resize(capacity_); + state.resize(capacity_); count_ = 0; // Insert will update it. removedCount_ = 0; - for (auto &iter : old) { - if (iter.state == BucketState::TAKEN) { - Insert(iter.key, iter.value); + for (size_t i = 0; i < old.size(); i++) { + if (oldState[i] == BucketState::TAKEN) { + Insert(old[i].key, old[i].value); } } } struct Pair { - BucketState state; Key key; Value value; }; std::vector map; + std::vector state; int capacity_; int count_ = 0; int removedCount_ = 0; @@ -176,6 +176,7 @@ class PrehashMap { public: PrehashMap(int initialCapacity) : capacity_(initialCapacity) { map.resize(initialCapacity); + state.resize(initialCapacity); } // Returns nullptr if no entry was found. @@ -185,9 +186,9 @@ public: // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN && hash == map[p].hash) + if (state[p] == BucketState::TAKEN && hash == map[p].hash) return map[p].value; - else if (map[p].state == BucketState::FREE) + else if (state[p] == BucketState::FREE) return NullValue; p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) @@ -205,8 +206,8 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = hash & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN) { if (hash == map[p].hash) return false; // Bad! } else { @@ -219,10 +220,10 @@ public: Crash(); } } - if (map[p].state == BucketState::REMOVED) { + if (state[p] == BucketState::REMOVED) { removedCount_--; } - map[p].state = BucketState::TAKEN; + state[p] = BucketState::TAKEN; map[p].hash = hash; map[p].value = value; count_++; @@ -233,10 +234,10 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = hash & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN && hash == map[p].hash) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN && hash == map[p].hash) { // Got it! - map[p].state = BucketState::REMOVED; + state[p] = BucketState::REMOVED; removedCount_++; count_--; return true; @@ -255,17 +256,15 @@ public: template void Iterate(T func) { - for (auto &iter : map) { - if (iter.state == BucketState::TAKEN) { - func(iter.hash, iter.value); + for (size_t i = 0; i < map.size(); i++) { + if (state[i] == BucketState::TAKEN) { + func(map[i].hash, map[i].value); } } } void Clear() { - // TODO: Speedup? - map.clear(); - map.resize(capacity_); + memset(state.data(), (int)BucketState::FREE, state.size()); } // Gets rid of REMOVED tombstones, making lookups somewhat more efficient. @@ -285,23 +284,24 @@ private: // We simply move out the existing data, then we re-insert the old. // This is extremely non-atomic and will need synchronization. std::vector old = std::move(map); + std::vector oldState = std::move(state); capacity_ *= factor; - map.clear(); map.resize(capacity_); + state.resize(capacity_); count_ = 0; // Insert will update it. removedCount_ = 0; - for (auto &iter : old) { - if (iter.state == BucketState::TAKEN) { - Insert(iter.hash, iter.value); + for (size_t i = 0; i < old.size(); i++) { + if (oldState[i] == BucketState::TAKEN) { + Insert(old[i].hash, old[i].value); } } } struct Pair { - BucketState state; uint32_t hash; Value value; }; std::vector map; + std::vector state; int capacity_; int count_ = 0; int removedCount_ = 0; diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 248ec0b643..3fcfcc49cb 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -153,156 +153,14 @@ VulkanContext::~VulkanContext() { VulkanFree(); } -void TransitionToPresent(VkCommandBuffer cmd, VkImage image) { - VkImageMemoryBarrier prePresentBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - prePresentBarrier.subresourceRange.baseMipLevel = 0; - prePresentBarrier.subresourceRange.levelCount = 1; - prePresentBarrier.subresourceRange.baseArrayLayer = 0; - prePresentBarrier.subresourceRange.layerCount = 1; - prePresentBarrier.image = image; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); -} - -void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { - VkImageMemoryBarrier prePresentBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - prePresentBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - prePresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - prePresentBarrier.subresourceRange.baseMipLevel = 0; - prePresentBarrier.subresourceRange.levelCount = 1; - prePresentBarrier.subresourceRange.baseArrayLayer = 0; - prePresentBarrier.subresourceRange.layerCount = 1; - prePresentBarrier.image = image; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); -} - -VkCommandBuffer VulkanContext::GetInitCommandBuffer() { +void VulkanContext::BeginFrame() { FrameData *frame = &frame_[curFrame_]; - if (!frame->hasInitCommands) { - VulkanBeginCommandBuffer(frame->cmdInit); - frame->hasInitCommands = true; - } - return frame_[curFrame_].cmdInit; -} - -void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) { - cmdQueue_.push_back(cmd); -} - -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); - - // Make sure the very last command buffer from the frame before the previous has been fully executed. - WaitAndResetFence(frame->fence); - // Process pending deletes. frame->deleteList.PerformDeletes(device_); - - // Reset both command buffers in one fell swoop. - // Note that on the first frame, there might already be commands so don't reset in that case. - if (!frame->hasInitCommands) { - vkResetCommandPool(device_, frame->cmdPool, 0); - } - - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - 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]; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = width_; - rp_begin.renderArea.extent.height = height_; - rp_begin.clearValueCount = 2; - rp_begin.pClearValues = clear_values; - 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); - assert(res == VK_SUCCESS); - - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - - int numCmdBufs = 0; - std::vector cmdBufs; - if (frame->hasInitCommands) { - vkEndCommandBuffer(frame->cmdInit); - cmdBufs.push_back(frame->cmdInit); - frame->hasInitCommands = false; - } - for (auto cmd : cmdQueue_) { - cmdBufs.push_back(cmd); - } - cmdQueue_.clear(); - cmdBufs.push_back(frame->cmdBuf); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - 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 = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; - res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence); - assert(res == VK_SUCCESS); - - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swap_chain_; - present.pImageIndices = ¤t_buffer; - present.pWaitSemaphores = &renderingCompleteSemaphore_; - present.waitSemaphoreCount = 1; - present.pResults = NULL; - - res = vkQueuePresentKHR(gfx_queue_, &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes - assert(!res); - - frame->deleteList.Take(globalDeleteList_); + frame_[curFrame_].deleteList.Take(globalDeleteList_); curFrame_++; if (curFrame_ >= inflightFrames_) { curFrame_ = 0; @@ -330,78 +188,19 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem return false; } -void VulkanBeginCommandBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - VkCommandBufferBeginInfo cmd_buf_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - cmd_buf_info.pInheritanceInfo = nullptr; - cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - res = vkBeginCommandBuffer(cmd, &cmd_buf_info); - assert(res == VK_SUCCESS); -} - -bool VulkanContext::InitObjects(bool depthPresent) { - int physical_device = 0; // TODO +bool VulkanContext::InitObjects() { InitQueue(); - // Create frame data - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkResult U_ASSERT_ONLY res; - VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - cmd_pool_info.queueFamilyIndex = graphics_queue_family_index_; - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; - - res = vkCreateCommandPool(device_, &cmd_pool_info, NULL, &frame_[i].cmdPool); - assert(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frame_[i].cmdPool; - cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 2; - - VkCommandBuffer cmdBuf[2]; - res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf); - assert(res == VK_SUCCESS); - frame_[i].cmdBuf = cmdBuf[0]; - frame_[i].cmdInit = cmdBuf[1]; - frame_[i].fence = CreateFence(true); // So it can be instantly waited on - } - - VkCommandBuffer cmd = GetInitCommandBuffer(); - if (!InitSwapchain(cmd)) { + if (!InitSwapchain()) { return false; } - InitDepthStencilBuffer(cmd); - - InitSurfaceRenderPass(depthPresent, true); - InitFramebuffers(depthPresent); - - // The init command buffer will be executed as part of the first frame. - return true; } void VulkanContext::DestroyObjects() { - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkCommandBuffer cmdBuf[2]; - cmdBuf[0] = frame_[i].cmdBuf; - cmdBuf[1] = frame_[i].cmdInit; - vkFreeCommandBuffers(device_, frame_[i].cmdPool, 2, cmdBuf); - vkDestroyCommandPool(device_, frame_[i].cmdPool, nullptr); - } - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - vkDestroyFence(device_, frame_[i].fence, nullptr); - } - - DestroyFramebuffers(); - DestroySurfaceRenderPass(); - DestroyDepthStencilBuffer(); - DestroySwapChain(); - - // If there happen to be any pending deletes, now is a good time. - for (int i = 0; i < ARRAY_SIZE(frame_); i++) { - frame_[i].deleteList.PerformDeletes(device_); - } - Delete().PerformDeletes(device_); + if (swapchain_ != VK_NULL_HANDLE) + vkDestroySwapchainKHR(device_, swapchain_, nullptr); + swapchain_ = VK_NULL_HANDLE; vkDestroySurfaceKHR(instance_, surface_, nullptr); surface_ = VK_NULL_HANDLE; @@ -692,77 +491,6 @@ void VulkanContext::DestroyDebugMsgCallback() { } } -void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - bool U_ASSERT_ONLY pass; - - const VkFormat depth_format = deviceInfo_.preferredDepthStencilFormat; - int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = depth_format; - image_info.extent.width = width_; - image_info.extent.height = height_; - image_info.extent.depth = 1; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = NULL; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - image_info.flags = 0; - - VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; - - VkMemoryRequirements mem_reqs; - - depth.format = depth_format; - - res = vkCreateImage(device_, &image_info, NULL, &depth.image); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(device_, depth.image, &mem_reqs); - - mem_alloc.allocationSize = mem_reqs.size; - /* Use the memory properties to determine the type of memory required */ - pass = MemoryTypeFromProperties(mem_reqs.memoryTypeBits, - 0, /* No requirements */ - &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(device_, &mem_alloc, NULL, &depth.mem); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(device_, depth.image, depth.mem, 0); - assert(res == VK_SUCCESS); - - TransitionImageLayout(cmd, depth.image, - aspectMask, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - - VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - depth_view_info.image = depth.image; - depth_view_info.format = depth_format; - depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; - depth_view_info.subresourceRange.aspectMask = aspectMask; - depth_view_info.subresourceRange.baseMipLevel = 0; - depth_view_info.subresourceRange.levelCount = 1; - depth_view_info.subresourceRange.baseArrayLayer = 0; - depth_view_info.subresourceRange.layerCount = 1; - depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - depth_view_info.flags = 0; - - res = vkCreateImageView(device_, &depth_view_info, NULL, &depth.view); - assert(res == VK_SUCCESS); -} - #ifdef _WIN32 void VulkanContext::InitSurfaceWin32(HINSTANCE conn, HWND wnd) { connection = conn; @@ -876,39 +604,32 @@ void VulkanContext::InitQueue() { // supported format will be returned. if (formatCount == 0 || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { ILOG("swapchain_format: Falling back to B8G8R8A8_UNORM"); - swapchain_format = VK_FORMAT_B8G8R8A8_UNORM; + swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM; } else { - swapchain_format = VK_FORMAT_UNDEFINED; + swapchainFormat_ = VK_FORMAT_UNDEFINED; for (uint32_t i = 0; i < formatCount; ++i) { if (surfFormats[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) { continue; } if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { - swapchain_format = surfFormats[i].format; + swapchainFormat_ = surfFormats[i].format; break; } } - if (swapchain_format == VK_FORMAT_UNDEFINED) { + if (swapchainFormat_ == VK_FORMAT_UNDEFINED) { // Okay, take the first one then. - swapchain_format = surfFormats[0].format; + swapchainFormat_ = surfFormats[0].format; } - ILOG("swapchain_format: %d (/%d)", swapchain_format, formatCount); + ILOG("swapchain_format: %d (/%d)", swapchainFormat_, formatCount); } delete[] surfFormats; vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_); ILOG("gfx_queue_: %p", gfx_queue_); - - VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphoreCreateInfo.flags = 0; - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &acquireSemaphore_); - assert(res == VK_SUCCESS); - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); - assert(res == VK_SUCCESS); } -bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { +bool VulkanContext::InitSwapchain() { VkResult U_ASSERT_ONLY res; VkSurfaceCapabilitiesKHR surfCapabilities; @@ -988,7 +709,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { VkSwapchainCreateInfoKHR swap_chain_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; swap_chain_info.surface = surface_; swap_chain_info.minImageCount = desiredNumberOfSwapChainImages; - swap_chain_info.imageFormat = swapchain_format; + swap_chain_info.imageFormat = swapchainFormat_; swap_chain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swap_chain_info.imageExtent.width = swapChainExtent.width; swap_chain_info.imageExtent.height = swapChainExtent.height; @@ -1009,140 +730,15 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; } - res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swap_chain_); + res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swapchain_); assert(res == VK_SUCCESS); if (res != VK_SUCCESS) { return false; } - res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, nullptr); - assert(res == VK_SUCCESS); - - 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); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - swap_chain_buffer sc_buffer; - - VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - color_image_view.format = swapchain_format; - color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; - color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; - color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; - color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; - color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - color_image_view.subresourceRange.baseMipLevel = 0; - color_image_view.subresourceRange.levelCount = 1; - color_image_view.subresourceRange.baseArrayLayer = 0; - color_image_view.subresourceRange.layerCount = 1; - color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; - color_image_view.flags = 0; - - sc_buffer.image = swapchainImages[i]; - - // TODO: Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring - // in image to render to will be to transition them away from that. - TransitionImageLayout(cmd, sc_buffer.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - color_image_view.image = sc_buffer.image; - - res = vkCreateImageView(device_, &color_image_view, NULL, &sc_buffer.view); - swapChainBuffers.push_back(sc_buffer); - assert(res == VK_SUCCESS); - } - - delete[] swapchainImages; - current_buffer = 0; return true; } -void VulkanContext::InitSurfaceRenderPass(bool include_depth, bool clear) { - // Need attachments for render target and depth buffer - VkAttachmentDescription attachments[2]{}; - attachments[0].format = swapchain_format; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : 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; - - if (include_depth) { - attachments[1].format = depth.format; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : 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 = nullptr; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = nullptr; - subpass.pDepthStencilAttachment = include_depth ? &depth_reference : nullptr; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.attachmentCount = include_depth ? 2 : 1; - rp_info.pAttachments = attachments; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = nullptr; - - VkResult U_ASSERT_ONLY res = vkCreateRenderPass(device_, &rp_info, nullptr, &surface_render_pass_); - assert(res == VK_SUCCESS); -} - -void VulkanContext::InitFramebuffers(bool include_depth) { - VkResult U_ASSERT_ONLY res; - VkImageView attachments[2]; - attachments[1] = depth.view; - - ILOG("InitFramebuffers: %dx%d", width_, height_); - VkFramebufferCreateInfo fb_info{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb_info.renderPass = surface_render_pass_; - fb_info.attachmentCount = include_depth ? 2 : 1; - fb_info.pAttachments = attachments; - fb_info.width = width_; - fb_info.height = height_; - fb_info.layers = 1; - - framebuffers_.resize(swapchainImageCount); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - attachments[0] = swapChainBuffers[i].view; - res = vkCreateFramebuffer(device_, &fb_info, nullptr, &framebuffers_[i]); - assert(res == VK_SUCCESS); - } -} - VkFence VulkanContext::CreateFence(bool presignalled) { VkFence fence; VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; @@ -1151,57 +747,20 @@ VkFence VulkanContext::CreateFence(bool presignalled) { return fence; } -void VulkanContext::WaitAndResetFence(VkFence fence) { - vkWaitForFences(device_, 1, &fence, true, UINT64_MAX); - vkResetFences(device_, 1, &fence); -} - -void VulkanContext::DestroyDepthStencilBuffer() { - if (depth.view != VK_NULL_HANDLE) - vkDestroyImageView(device_, depth.view, nullptr); - if (depth.image != VK_NULL_HANDLE) - vkDestroyImage(device_, depth.image, nullptr); - if (depth.mem != VK_NULL_HANDLE) - vkFreeMemory(device_, depth.mem, nullptr); - - depth.view = VK_NULL_HANDLE; - depth.image = VK_NULL_HANDLE; - depth.mem = VK_NULL_HANDLE; -} - -void VulkanContext::DestroySwapChain() { - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device_, swapChainBuffers[i].view, nullptr); - } - if (swap_chain_ != VK_NULL_HANDLE) - vkDestroySwapchainKHR(device_, swap_chain_, nullptr); - swap_chain_ = VK_NULL_HANDLE; - swapChainBuffers.clear(); - vkDestroySemaphore(device_, acquireSemaphore_, nullptr); - vkDestroySemaphore(device_, renderingCompleteSemaphore_, nullptr); -} - -void VulkanContext::DestroyFramebuffers() { - for (uint32_t i = 0; i < framebuffers_.size(); i++) { - vkDestroyFramebuffer(device_, framebuffers_[i], nullptr); - } - framebuffers_.clear(); -} - -void VulkanContext::DestroySurfaceRenderPass() { - if (surface_render_pass_ != VK_NULL_HANDLE) - vkDestroyRenderPass(device_, surface_render_pass_, nullptr); - surface_render_pass_ = VK_NULL_HANDLE; -} - void VulkanContext::DestroyDevice() { + // If there happen to be any pending deletes, now is a good time. + for (int i = 0; i < ARRAY_SIZE(frame_); i++) { + frame_[i].deleteList.PerformDeletes(device_); + } + Delete().PerformDeletes(device_); + vkDestroyDevice(device_, nullptr); device_ = nullptr; } VkPipelineCache VulkanContext::CreatePipelineCache() { VkPipelineCache cache; - VkPipelineCacheCreateInfo pc = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; + VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; pc.pInitialData = nullptr; pc.initialDataSize = 0; pc.flags = 0; @@ -1211,7 +770,7 @@ VkPipelineCache VulkanContext::CreatePipelineCache() { } bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule) { - VkShaderModuleCreateInfo sm = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; sm.pCode = spirv.data(); sm.codeSize = spirv.size() * sizeof(uint32_t); sm.flags = 0; @@ -1223,67 +782,25 @@ bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkSha } } -void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout) { - VkImageMemoryBarrier image_memory_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = 0; - image_memory_barrier.oldLayout = old_image_layout; - image_memory_barrier.newLayout = new_image_layout; +void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) { + VkImageMemoryBarrier image_memory_barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + image_memory_barrier.srcAccessMask = srcAccessMask; + image_memory_barrier.dstAccessMask = dstAccessMask; + image_memory_barrier.oldLayout = oldImageLayout; + image_memory_barrier.newLayout = newImageLayout; image_memory_barrier.image = image; image_memory_barrier.subresourceRange.aspectMask = aspectMask; image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.layerCount = 1; - 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; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_HOST_WRITE_BIT; - } - image_memory_barrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - /* Make sure anything that was copying from this image has completed */ - image_memory_barrier.dstAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - /* Make sure any Copy or CPU writes to image are flushed */ - if (old_image_layout != VK_IMAGE_LAYOUT_UNDEFINED) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - image_memory_barrier.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT; - } - - VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - - vkCmdPipelineBarrier(cmd, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + image_memory_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) { switch (shader_type) { case VK_SHADER_STAGE_VERTEX_BIT: diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index d9ee98ba9b..f647f4d0f2 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -195,8 +195,7 @@ private: std::vector callbacks_; }; -// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc. -// Optionally, it can create a depth buffer for you as well. +// VulkanContext manages the device and swapchain, and deferred deletion of objects. class VulkanContext { public: VulkanContext(); @@ -227,19 +226,12 @@ public: void ReinitSurfaceAndroid(int width, int height); #endif void InitQueue(); - bool InitObjects(bool depthPresent); - bool InitSwapchain(VkCommandBuffer cmd); - void InitSurfaceRenderPass(bool include_depth, bool clear); - void InitFramebuffers(bool include_depth); - void InitDepthStencilBuffer(VkCommandBuffer cmd); + bool InitObjects(); + bool InitSwapchain(); // Also destroys the surface. void DestroyObjects(); - void DestroySurfaceRenderPass(); - void DestroyFramebuffers(); - void DestroySwapChain(); - void DestroyDepthStencilBuffer(); void DestroyDevice(); void WaitUntilQueueIdle(); @@ -248,41 +240,17 @@ public: VkFence CreateFence(bool presignalled); bool CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule); - void WaitAndResetFence(VkFence fence); + int GetBackbufferWidth() { return width_; } + int GetBackbufferHeight() { return height_; } - int GetWidth() { return width_; } - int GetHeight() { return height_; } - - VkCommandBuffer GetInitCommandBuffer(); - - VkFramebuffer GetSurfaceFramebuffer() { - return framebuffers_[current_buffer]; - } - // This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass. - VkCommandBuffer GetSurfaceCommandBuffer() { - return frame_[curFrame_].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 BeginFrame(); void EndFrame(); - void QueueBeforeSurfaceRender(VkCommandBuffer cmd); - bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr); void DestroyDebugMsgCallback(); - VkRenderPass GetSurfaceRenderPass() const { - return surface_render_pass_; - } - VkPhysicalDevice GetPhysicalDevice(int n = 0) const { return physical_devices_[n]; } @@ -327,8 +295,21 @@ public: return inflightFrames_; } + int GetCurFrame() const { + return curFrame_; + } + + VkSwapchainKHR GetSwapchain() const { + return swapchain_; + } + VkFormat GetSwapchainFormat() const { + return swapchainFormat_; + } + + // 1 for no frame overlap and thus minimal latency but worst performance. + // 2 is an OK compromise, while 3 performs best but risks slightly higher latency. enum { - MAX_INFLIGHT_FRAMES = 2, + MAX_INFLIGHT_FRAMES = 3, }; private: @@ -340,9 +321,6 @@ private: bool CheckLayers(const std::vector &layer_props, const std::vector &layer_names) const; - VkSemaphore acquireSemaphore_; - VkSemaphore renderingCompleteSemaphore_; - #ifdef _WIN32 HINSTANCE connection = nullptr; // hInstance - Windows Instance HWND window = nullptr; // hWnd - window handle @@ -380,36 +358,15 @@ private: // Custom collection of things that are good to know VulkanPhysicalDeviceInfo deviceInfo_; - struct swap_chain_buffer { - VkImage image; - VkImageView view; - }; - // Swap chain int width_ = 0; int height_ = 0; int flags_ = 0; - VkFormat swapchain_format = VK_FORMAT_UNDEFINED; - std::vector framebuffers_; - uint32_t swapchainImageCount = 0; - VkSwapchainKHR swap_chain_ = VK_NULL_HANDLE; - std::vector swapChainBuffers; int inflightFrames_ = MAX_INFLIGHT_FRAMES; - // Manages flipping command buffers for the backbuffer render pass. - // It is recommended to do the same for other rendering passes. struct FrameData { - FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {} - - VkFence fence; - bool hasInitCommands; - - // TODO: Move to frame data - VkCommandPool cmdPool; - VkCommandBuffer cmdInit; - VkCommandBuffer cmdBuf; - + FrameData() {} VulkanDeleteList deleteList; }; FrameData frame_[MAX_INFLIGHT_FRAMES]; @@ -421,15 +378,9 @@ private: std::vector msg_callbacks; - struct { - VkFormat format; - VkImage image = VK_NULL_HANDLE; - VkDeviceMemory mem = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - } depth; + VkSwapchainKHR swapchain_; + VkFormat swapchainFormat_; - VkRenderPass surface_render_pass_ = VK_NULL_HANDLE; - uint32_t current_buffer = 0; uint32_t queue_count = 0; VkPhysicalDeviceFeatures featuresAvailable_; @@ -438,14 +389,11 @@ private: std::vector cmdQueue_; }; -// Stand-alone utility functions -void VulkanBeginCommandBuffer(VkCommandBuffer cmd); -void TransitionImageLayout( - VkCommandBuffer cmd, - VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout old_image_layout, - VkImageLayout new_image_layout); +// Detailed control. +void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); // GLSL compiler void init_glslang(); diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 7fca19628d..75f78f95b4 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -8,13 +8,6 @@ VkResult VulkanTexture::Create(int w, int h, VkFormat format) { VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps); - - // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data. - // Linear tiling is usually only supported for 2D non-array textures. - // needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; - // Always stage. - needStaging = true; - return VK_SUCCESS; } @@ -39,7 +32,7 @@ void VulkanTexture::CreateMappableImage() { image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; - image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -91,22 +84,13 @@ uint8_t *VulkanTexture::Lock(int level, int *rowPitch) { return (uint8_t *)data; } -void VulkanTexture::Unlock() { +void VulkanTexture::Unlock(VkCommandBuffer cmd) { vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - // if we already have an image, queue it for destruction and forget it. Wipe(); - if (!needStaging) { - // If we can use the linear tiled image as a texture, just do it - image = mappableImage; - mem = mappableMemory; - TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - // Make sure we don't accidentally delete the main image. - mappableImage = VK_NULL_HANDLE; - mappableMemory = VK_NULL_HANDLE; - } else { + + { // Shrink the diff by not unindenting. If you make major changes, remove this. VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = format_; @@ -145,15 +129,17 @@ void VulkanTexture::Unlock() { assert(res == VK_SUCCESS); // Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL - TransitionImageLayout(cmd, mappableImage, + TransitionImageLayout2(cmd, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT); VkImageCopy copy_region; copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -183,10 +169,11 @@ void VulkanTexture::Unlock() { assert(res == VK_SUCCESS); // Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); // Then drop the temporary mappable image - although should not be necessary... vulkan_->Delete().QueueDeleteImage(mappableImage); @@ -238,11 +225,9 @@ static bool IsDepthStencilFormat(VkFormat format) { } } -bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { +bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { Wipe(); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - tex_width = w; tex_height = h; numMips_ = numMips; @@ -276,7 +261,19 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI // Write a command to transition the image to the requested layout, if it's not already that layout. if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) { - TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, initialLayout); + switch (initialLayout) { + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT); + break; + default: + // If you planned to use UploadMip, you want VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. After the + // upload, you can transition. + assert(false); + break; + } } vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); @@ -337,7 +334,7 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI return true; } -void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { +void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { VkBufferImageCopy copy_region = {}; copy_region.bufferOffset = offset; copy_region.bufferRowLength = (uint32_t)rowLength; @@ -350,24 +347,15 @@ void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buf copy_region.imageSubresource.baseArrayLayer = 0; copy_region.imageSubresource.layerCount = 1; - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); } -void VulkanTexture::EndCreate() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - TransitionImageLayout(cmd, image, +void VulkanTexture::EndCreate(VkCommandBuffer cmd) { + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 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); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } void VulkanTexture::Destroy() { diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 3b67119946..cc303a7460 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -11,7 +11,7 @@ public: VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator = nullptr) : vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE), tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED), - mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), needStaging(false), + mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), allocator_(allocator), offset_(0) { memset(&mem_reqs, 0, sizeof(mem_reqs)); } @@ -26,16 +26,14 @@ public: // been called. VkResult Create(int w, int h, VkFormat format); uint8_t *Lock(int level, int *rowPitch); - void Unlock(); + void Unlock(VkCommandBuffer cmd); // Fast uploads from buffer. Mipmaps supported. // Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. // When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. - 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(); + bool CreateDirect(VkCommandBuffer cmd, 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(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels + void EndCreate(VkCommandBuffer cmd); int GetNumMips() const { return numMips_; } void Destroy(); @@ -64,5 +62,4 @@ private: VkMemoryRequirements mem_reqs; VulkanDeviceAllocator *allocator_; size_t offset_; - bool needStaging; }; diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index c6db161c8a..260be7d9d6 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -47,10 +47,16 @@ bool VulkanPushBuffer::AddBuffer() { return false; } + // Make validation happy. + VkMemoryRequirements reqs; + vkGetBufferMemoryRequirements(device_, info.buffer, &reqs); + // TODO: We really should use memoryTypeIndex here.. + // Okay, that's the buffer. Now let's allocate some memory for it. VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + // TODO: Should check here that memoryTypeIndex_ matches reqs.memoryTypeBits. alloc.memoryTypeIndex = memoryTypeIndex_; - alloc.allocationSize = size_; + alloc.allocationSize = reqs.size; res = vkAllocateMemory(device_, &alloc, nullptr, &info.deviceMemory); if (VK_SUCCESS != res) { diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index dda064edaa..cdb0667fc8 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -236,7 +236,10 @@ - + + MaxSpeed + Default + diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index a57de30af7..227ae76093 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -35,6 +35,9 @@ const static u32 GAMEDATA_BYTES_PER_READ = 32768; // If this is too high, some games (e.g. Senjou no Valkyria 3) will lag. const static u32 GAMEDATA_READS_PER_UPDATE = 20; +const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_WRITE_PROTECTED = 0x80111903; +const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_REMOVED = 0x80111901; + static const std::string SFO_FILENAME = "PARAM.SFO"; namespace @@ -95,6 +98,9 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) { int PSPGamedataInstallDialog::Update(int animSpeed) { if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) return SCE_ERROR_UTILITY_INVALID_STATUS; + + // TODO: This should return error codes in some cases, like write failure. + // request.common.result must be updated for errors as well. if (readFiles < numFiles) { if (currentInputFile != 0 && currentOutputFile != 0) { @@ -244,7 +250,7 @@ void PSPGamedataInstallDialog::UpdateProgress() { } void PSPGamedataInstallDialog::DoState(PointerWrap &p) { - auto s = p.Section("PSPGamedataInstallDialog", 0, 3); + auto s = p.Section("PSPGamedataInstallDialog", 0, 4); if (!s) return; @@ -252,8 +258,8 @@ void PSPGamedataInstallDialog::DoState(PointerWrap &p) { PSPDialog::DoState(p); p.Do(request); - // This was included in version 2 and higher. - if (s > 2) { + // This was included in version 2 and higher, but for BC reasons we use 3+. + if (s >= 3) { p.Do(param.ptr); p.Do(inFileNames); p.Do(numFiles); @@ -265,7 +271,7 @@ void PSPGamedataInstallDialog::DoState(PointerWrap &p) { param.ptr = 0; } - if (s > 3) { + if (s >= 4) { p.Do(currentInputFile); p.Do(currentInputBytesLeft); p.Do(currentOutputFile); diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 518ca2797a..cf09c4db42 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -143,6 +143,18 @@ static const wchar_t oskKeys[OSK_KEYBOARD_COUNT][5][14] = }, }; +// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards. +int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = { + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, + PSP_UTILITY_OSK_INPUTTYPE_KOREAN, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, +}; PSPOskDialog::PSPOskDialog() : PSPDialog() { // This can break all kinds of stuff, changing the decimal point in sprintf for example. @@ -227,6 +239,42 @@ void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const wchar_t *input) _string = stringBuffer; } +static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) { + OskKeyboardLanguage origLang = lang; + OskKeyboardDisplay origDisp = disp; + + if (inputType == 0) { + return; + } + + // TODO: Limit by allowed keyboards properly... this is just an approximation. + int tries = OSK_LANGUAGE_COUNT * 2; + while (!(inputType & allowedInputFlagsMap[disp]) && tries > 0) { + if ((--tries % 1) == 0) { + lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT); + disp = OskKeyboardCases[lang][LOWERCASE]; + } else { + disp = OskKeyboardCases[lang][UPPERCASE]; + } + } + + if (tries == 0) { + // In case of error, let's just fall back to allowing all. + lang = origLang; + disp = origDisp; + } +} + +static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) { + // Swap disp and check if it's valid. + if (disp == OskKeyboardCases[lang][UPPERCASE]) + disp = OskKeyboardCases[lang][LOWERCASE]; + else + disp = OskKeyboardCases[lang][UPPERCASE]; + + return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0; +} + int PSPOskDialog::Init(u32 oskPtr) { // Ignore if already running if (GetStatus() != SCE_UTILITY_STATUS_NONE) { @@ -259,8 +307,9 @@ int PSPOskDialog::Init(u32 oskPtr) { ChangeStatusInit(OSK_INIT_DELAY_US); selectedChar = 0; - currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; + currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; + FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc); ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext); @@ -887,37 +936,43 @@ int PSPOskDialog::Update(int animSpeed) { PPGeDrawText("Start", 135, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("Finish"), 185, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - int index = (currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT; - const char *countryCode; + auto lookupLangName = [&](int direction) { + // First, find the valid one... + OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT); + OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp); - if (index >= 0) - countryCode = OskKeyboardNames[(currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT].c_str(); - else - countryCode = OskKeyboardNames[OSK_LANGUAGE_COUNT - 1].c_str(); + if (lang == currentKeyboardLanguage) { + return (const char *)nullptr; + } - const char *language = languageMapping[countryCode].first.c_str(); + // Now, let's grab the name. + const char *countryCode = OskKeyboardNames[lang].c_str(); + const char *language = languageMapping[countryCode].first.c_str(); - if (!strcmp(countryCode, "English Full-width")) - language = "English Full-width"; - - countryCode = OskKeyboardNames[currentKeyboardLanguage].c_str(); - - if (strcmp(countryCode, "ko_KR")) { + // It seems like this is a "fake" country code for extra keyboard purposes. + if (!strcmp(countryCode, "English Full-width")) + language = "English Full-width"; + + return language; + }; + + if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { PPGeDrawText("Select", 135, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("Shift"), 185, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); } - - PPGeDrawText("L", 235, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText(language, 255, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - countryCode = OskKeyboardNames[(currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT].c_str(); - language = languageMapping[countryCode].first.c_str(); + const char *prevLang = lookupLangName(-1); + if (prevLang) { + PPGeDrawText("L", 235, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText(prevLang, 255, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + } - if (!strcmp(countryCode, "English Full-width")) - language = "English Full-width"; - - PPGeDrawText("R", 235, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText(language, 255, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + const char *nextLang = lookupLangName(1); + if (nextLang) { + PPGeDrawText("R", 235, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText(nextLang, 255, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + } if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { selectedChar -= numKeyCols[currentKeyboard]; @@ -939,10 +994,12 @@ int PSPOskDialog::Update(int animSpeed) { inputChars = CombinationString(true); } else if (IsButtonPressed(CTRL_SELECT)) { // Select now swaps case. - if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; - else - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; + if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { + if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + else + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; + } if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; @@ -954,10 +1011,10 @@ int PSPOskDialog::Update(int animSpeed) { selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; } else if (IsButtonPressed(CTRL_RTRIGGER)) { - // TODO: Limit by allowed keyboards... // RTRIGGER now cycles languages forward. currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT); currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; @@ -969,14 +1026,13 @@ int PSPOskDialog::Update(int animSpeed) { selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; } else if (IsButtonPressed(CTRL_LTRIGGER)) { - // TODO: Limit by allowed keyboards... // LTRIGGER now cycles languages backward. if (currentKeyboardLanguage - 1 >= 0) currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT); else currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1); - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard); if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 0640eca06b..ac506478bc 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -903,7 +903,7 @@ int SavedataParam::BuildHash(unsigned char *output, return 0; } -std::string SavedataParam::GetSpaceText(u64 size) +std::string SavedataParam::GetSpaceText(u64 size, bool roundUp) { static const char *suffixes[] = {"B", "KB", "MB", "GB"}; char text[50]; @@ -915,7 +915,11 @@ std::string SavedataParam::GetSpaceText(u64 size) snprintf(text, sizeof(text), "%lld %s", size, suffixes[i]); return std::string(text); } - size /= 1024; + if (roundUp) { + size = (size + 1023) / 1024; + } else { + size /= 1024; + } } snprintf(text, sizeof(text), "%llu TB", size); @@ -936,7 +940,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) param->msFree->clusterSize = (u32)MemoryStick_SectorSize(); param->msFree->freeClusters = (u32)(freeBytes / MemoryStick_SectorSize()); param->msFree->freeSpaceKB = (u32)(freeBytes / 0x400); - const std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes); + const std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes, false); memset(param->msFree->freeSpaceStr, 0, sizeof(param->msFree->freeSpaceStr)); strncpy(param->msFree->freeSpaceStr, spaceTxt.c_str(), sizeof(param->msFree->freeSpaceStr)); } @@ -959,7 +963,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) // Fieldrunners expects 736 KB, even though the files add up to ~600 KB. int total_size = param->msData->info.usedClusters * (u32)MemoryStick_SectorSize(); param->msData->info.usedSpaceKB = total_size / 0x400; - std::string spaceTxt = SavedataParam::GetSpaceText(total_size); + std::string spaceTxt = SavedataParam::GetSpaceText(total_size, true); strncpy(param->msData->info.usedSpaceStr, spaceTxt.c_str(), sizeof(param->msData->info.usedSpaceStr)); // TODO: What does this mean, then? Seems to be the same. @@ -1001,13 +1005,13 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) param->utilityData->usedClusters = total_size / (u32)MemoryStick_SectorSize(); param->utilityData->usedSpaceKB = total_size / 0x400; - std::string spaceTxt = SavedataParam::GetSpaceText(total_size); + std::string spaceTxt = SavedataParam::GetSpaceText(total_size, true); memset(param->utilityData->usedSpaceStr, 0, sizeof(param->utilityData->usedSpaceStr)); strncpy(param->utilityData->usedSpaceStr, spaceTxt.c_str(), sizeof(param->utilityData->usedSpaceStr)); // TODO: Maybe these are rounded to the nearest 32KB? Or something? param->utilityData->usedSpace32KB = total_size / 0x400; - spaceTxt = SavedataParam::GetSpaceText(total_size); + spaceTxt = SavedataParam::GetSpaceText(total_size, true); memset(param->utilityData->usedSpace32Str, 0, sizeof(param->utilityData->usedSpace32Str)); strncpy(param->utilityData->usedSpace32Str, spaceTxt.c_str(), sizeof(param->utilityData->usedSpace32Str)); } @@ -1188,29 +1192,62 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) return false; } - std::string saveDir = savePath + GetGameName(param) + GetSaveName(param); + const std::string saveDir = savePath + GetGameName(param) + GetSaveName(param); PSPFileInfo info = pspFileSystem.GetFileInfo(saveDir); bool exists = info.exists; if (param->sizeInfo.IsValid()) { const u64 freeBytes = MemoryStick_FreeSpace(); - // TODO: Read the entries and count up the size vs. existing size? + + s64 overwriteBytes = 0; + s64 writeBytes = 0; + for (int i = 0; i < param->sizeInfo->numNormalEntries; ++i) { + const auto &entry = param->sizeInfo->normalEntries[i]; + overwriteBytes += pspFileSystem.GetFileInfo(saveDir + "/" + entry.name).size; + writeBytes += entry.size; + } + for (int i = 0; i < param->sizeInfo->numSecureEntries; ++i) { + const auto &entry = param->sizeInfo->secureEntries[i]; + overwriteBytes += pspFileSystem.GetFileInfo(saveDir + "/" + entry.name).size; + writeBytes += entry.size + 0x10; + } param->sizeInfo->sectorSize = (int)MemoryStick_SectorSize(); param->sizeInfo->freeSectors = (int)(freeBytes / MemoryStick_SectorSize()); - // TODO: Is this after the specified files? Before? + // TODO: Is this after the specified files? Probably before? param->sizeInfo->freeKB = (int)(freeBytes / 1024); - std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes); - strncpy(param->sizeInfo->freeString, spaceTxt.c_str(), 8); - param->sizeInfo->freeString[7] = '\0'; + std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes, false); + truncate_cpy(param->sizeInfo->freeString, spaceTxt.c_str()); - // TODO. - param->sizeInfo->neededKB = 0; - strcpy(param->sizeInfo->neededString, "0 KB"); - param->sizeInfo->overwriteKB = 0; - strcpy(param->sizeInfo->overwriteString, "0 KB"); + if (writeBytes - overwriteBytes < (s64)freeBytes) { + param->sizeInfo->neededKB = 0; + + // Note: this is "needed to overwrite". + param->sizeInfo->overwriteKB = 0; + + spaceTxt = GetSpaceText(0, true); + truncate_cpy(param->sizeInfo->neededString, spaceTxt.c_str()); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } else { + // Bytes needed to save additional data. + s64 neededBytes = writeBytes - freeBytes; + param->sizeInfo->neededKB = (neededBytes + 1023) / 1024; + spaceTxt = GetSpaceText(neededBytes, true); + truncate_cpy(param->sizeInfo->neededString, spaceTxt.c_str()); + + if (writeBytes - overwriteBytes < (s64)freeBytes) { + param->sizeInfo->overwriteKB = 0; + spaceTxt = GetSpaceText(0, true); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } else { + s64 neededOverwriteBytes = writeBytes - freeBytes - overwriteBytes; + param->sizeInfo->overwriteKB = (neededOverwriteBytes + 1023) / 1024; + spaceTxt = GetSpaceText(neededOverwriteBytes, true); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } + } } return exists; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index be8c679305..cccfbfc108 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -323,7 +323,7 @@ public: std::string GetKey(const SceUtilitySavedataParam *param) const; bool HasKey(const SceUtilitySavedataParam *param) const; - static std::string GetSpaceText(u64 size); + static std::string GetSpaceText(u64 size, bool roundUp); int SetPspParam(SceUtilitySavedataParam* param); SceUtilitySavedataParam *GetPspParam(); diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index b3d860870f..340d26ded3 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -44,7 +44,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang WRITE(p, "layout(set = 0, binding = 0) uniform sampler2D tex;\n"); WRITE(p, "layout(set = 0, binding = 1) uniform sampler2D pal;\n"); WRITE(p, "layout(location = 0) in vec2 v_texcoord0;\n"); - WRITE(p, "layout(location = 0) out vec4 fragColor0\n;"); + WRITE(p, "layout(location = 0) out vec4 fragColor0;\n"); } else { if (gl_extensions.IsGLES) { WRITE(p, "#version 300 es\n"); @@ -288,4 +288,4 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag } } -#undef WRITE \ No newline at end of file +#undef WRITE diff --git a/GPU/Common/DepalettizeShaderCommon.h b/GPU/Common/DepalettizeShaderCommon.h index 7d2b02d113..933d4c02d9 100644 --- a/GPU/Common/DepalettizeShaderCommon.h +++ b/GPU/Common/DepalettizeShaderCommon.h @@ -20,4 +20,6 @@ #include "GPU/ge_constants.h" #include "GPU/Common/ShaderCommon.h" +static const int DEPAL_TEXTURE_OLD_AGE = 120; + void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language); diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 20b581def7..99023d63f1 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -71,6 +71,10 @@ public: void SetupVertexDecoder(u32 vertType); + bool IsCodePtrVertexDecoder(const u8 *ptr) const { + return decJitCache_->IsInSpace(ptr); + } + protected: virtual void ClearTrackedVertexArrays() {} diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 27219635b3..25b2495215 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -32,6 +32,7 @@ #include "Core/ELF/ParamSFO.h" #include "Core/System.h" #include "Core/HLE/sceDisplay.h" +#include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/PostShader.h" #include "GPU/Common/TextureCacheCommon.h" @@ -117,6 +118,22 @@ FramebufferManagerCommon::FramebufferManagerCommon(Draw::DrawContext *draw) } FramebufferManagerCommon::~FramebufferManagerCommon() { + DecimateFBOs(); + for (auto vfb : vfbs_) { + DestroyFramebuf(vfb); + } + vfbs_.clear(); + + for (auto &tempFB : tempFBOs_) { + tempFB.second.fbo->Release(); + } + tempFBOs_.clear(); + + // Do the same for ReadFramebuffersToMemory's VFBs + for (auto vfb : bvfbs_) { + DestroyFramebuf(vfb); + } + bvfbs_.clear(); } void FramebufferManagerCommon::Init() { @@ -140,7 +157,7 @@ bool FramebufferManagerCommon::UpdateSize() { void FramebufferManagerCommon::BeginFrame() { DecimateFBOs(); - currentRenderVfb_ = 0; + currentRenderVfb_ = nullptr; updateVRAM_ = !(g_Config.iRenderingMode == FB_NON_BUFFERED_MODE || g_Config.iRenderingMode == FB_BUFFERED_MODE); } @@ -309,7 +326,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame bool vfbFormatChanged = false; // Find a matching framebuffer - VirtualFramebuffer *vfb = 0; + VirtualFramebuffer *vfb = nullptr; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *v = vfbs_[i]; if (v->fb_address == params.fb_address) { @@ -811,7 +828,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) { } void FramebufferManagerCommon::CopyDisplayToOutput() { - DownloadFramebufferOnSwitch(currentRenderVfb_); + // DownloadFramebufferOnSwitch(currentRenderVfb_); currentRenderVfb_ = 0; @@ -1045,6 +1062,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { void FramebufferManagerCommon::DecimateFBOs() { currentRenderVfb_ = 0; + for (auto iter : fbosToDelete_) { + delete iter; + } + fbosToDelete_.clear(); + for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used); @@ -1092,6 +1114,9 @@ void FramebufferManagerCommon::DecimateFBOs() { void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, u16 h, bool force, bool skipCopy) { VirtualFramebuffer old = *vfb; + int oldWidth = vfb->bufferWidth; + int oldHeight = vfb->bufferHeight; + if (force) { vfb->bufferWidth = w; vfb->bufferHeight = h; @@ -1148,14 +1173,14 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); if (old.fbo) { - INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, w, h, vfb->format); + INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format); if (vfb->fbo) { 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); + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0); } } - delete old.fbo; + fbosToDelete_.push_back(old.fbo); if (needGLESRebinds_) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } @@ -1800,8 +1825,6 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBCo if (!fbo) return fbo; - // 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; @@ -1838,3 +1861,246 @@ void FramebufferManagerCommon::ShowScreenResolution() { host->NotifyUserMessage(messageStream.str(), 2.0f, 0xFFFFFF, "resize"); INFO_LOG(SYSTEM, "%s", messageStream.str().c_str()); } + +// We might also want to implement an asynchronous callback-style version of this. Would probably +// only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers +// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar. +// +// The main use cases for this are: +// * GE debugging(in practice async will not matter because it will stall anyway.) +// * Video file recording(would probably be great if it was async.) +// * Screenshots(benefit slightly from async.) +// * Save state screenshots(could probably be async but need to manage the stall.) +bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); + return true; + } + + int w = vfb->renderWidth, h = vfb->renderHeight; + + Draw::Framebuffer *bound = nullptr; + + if (vfb->fbo) { + if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { + w = vfb->width * maxRes; + h = vfb->height * maxRes; + + Draw::Framebuffer *tempFBO = GetTempFBO(w, h); + VirtualFramebuffer tempVfb = *vfb; + tempVfb.fbo = tempFBO; + tempVfb.bufferWidth = vfb->width; + tempVfb.bufferHeight = vfb->height; + tempVfb.renderWidth = w; + tempVfb.renderHeight = h; + BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); + + bound = tempFBO; + } else { + bound = vfb->fbo; + } + } + + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); + } + + // TODO: Maybe should handle flipY inside CopyFramebufferToMemorySync somehow? + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + buffer.Allocate(w, h, GE_FORMAT_8888, flipY, true); + bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + // We may have blitted to a temp FBO. + RebindFramebuffer(); + return retval; +} + +bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); + return true; + } + + int w = vfb->renderWidth; + int h = vfb->renderHeight; + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); + } + + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { + buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256, flipY); + } else { + buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT, flipY); + } + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w); + return true; +} + +bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + // TODO: Actually get the stencil. + buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); + return true; + } + + int w = vfb->renderWidth; + int h = vfb->renderHeight; + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); + } + + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT, flipY); + if (draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w)) { + return true; + } else { + buffer.Free(); + return false; + } +} + +bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { + int w, h; + draw_->GetFramebufferDimensions(nullptr, &w, &h); + buffer.Allocate(w, h, GE_FORMAT_8888, false, true); + draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + return true; +} + +// This function takes an already correctly-sized framebuffer and packs it into RAM. +// Does not need to account for scaling. +// Color conversion is currently done on CPU but should theoretically be done on GPU. +// (Except using the GPU might cause problems because of various implementations' +// dithering behavior and games that expect exact colors like Danganronpa, so we +// can't entirely be rid of the CPU path.) -- unknown +void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); + return; + } + + const u32 fb_address = (0x04000000) | vfb->fb_address; + + Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); + const int dstBpp = (int)DataFormatSizeInBytes(destFormat); + + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); + + // We always need to convert from the framebuffer native format. + // Right now that's always 8888. + DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); + + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); +} + +void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { + // Clamp to width. Sometimes block transfers can cause this to hit. + if (x + w >= vfb->width) { + w = vfb->width - x; + } + if (vfb) { + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + OptimizeDownloadRange(vfb, x, y, w, h); + if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { + // No need to blit + PackFramebufferSync_(vfb, x, y, w, h); + } else { + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + PackFramebufferSync_(nvfb, x, y, w, h); + } + + textureCache_->ForgetLastTexture(); + RebindFramebuffer(); + } +} + +void FramebufferManagerCommon::FlushBeforeCopy() { + // Flush anything not yet drawn before blitting, downloading, or uploading. + // This might be a stalled list, or unflushed before a block transfer, etc. + + // TODO: It's really bad that we are calling SetRenderFramebuffer here with + // 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_->DispatchFlush(); +} + +void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { + VirtualFramebuffer *vfb = GetVFBAt(fb_address); + if (vfb && vfb->fb_stride != 0) { + const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; + int x = 0; + int y = 0; + int pixels = loadBytes / bpp; + // The height will be 1 for each stride or part thereof. + int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); + int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); + + // We might still have a pending draw to the fb in question, flush if so. + FlushBeforeCopy(); + + // No need to download if we already have it. + if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { + // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. + // CLUT framebuffers are often incorrectly estimated in size. + if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { + vfb->memoryUpdated = true; + } + vfb->clutUpdatedBytes = loadBytes; + + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + + PackFramebufferSync_(nvfb, x, y, w, h); + + textureCache_->ForgetLastTexture(); + RebindFramebuffer(); + } + } +} + +std::vector FramebufferManagerCommon::GetFramebufferList() { + std::vector list; + + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + + FramebufferInfo info; + info.fb_address = vfb->fb_address; + info.z_address = vfb->z_address; + info.format = vfb->format; + info.width = vfb->width; + info.height = vfb->height; + info.fbo = vfb->fbo; + list.push_back(info); + } + + return list; +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 27aef50cab..62befe6913 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -25,6 +25,7 @@ #include "Core/MemMap.h" #include "GPU/GPU.h" #include "GPU/ge_constants.h" +#include "GPU/GPUInterface.h" #include "thin3d/thin3d.h" enum { @@ -159,6 +160,21 @@ enum DrawTextureFlags { DRAWTEX_KEEP_TEX = 2, }; +inline Draw::DataFormat GEFormatToThin3D(int geFormat) { + switch (geFormat) { + case GE_FORMAT_4444: + return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; + case GE_FORMAT_5551: + return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; + case GE_FORMAT_565: + return Draw::DataFormat::R5G6B5_UNORM_PACK16; + case GE_FORMAT_8888: + return Draw::DataFormat::R8G8B8A8_UNORM; + default: + return Draw::DataFormat::UNDEFINED; + } +} + namespace Draw { class DrawContext; } @@ -166,6 +182,7 @@ class DrawContext; struct GPUDebugBuffer; class TextureCacheCommon; class ShaderManagerCommon; +class DrawEngineCommon; class FramebufferManagerCommon { public: @@ -173,7 +190,7 @@ public: virtual ~FramebufferManagerCommon(); virtual void Init(); - void BeginFrame(); + virtual void BeginFrame(); void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); void DestroyFramebuf(VirtualFramebuffer *v); @@ -192,10 +209,13 @@ public: FramebufferHeuristicParams inputs; GetFramebufferHeuristicInputs(&inputs, gstate); VirtualFramebuffer *vfb = DoSetRenderFrameBuffer(inputs, skipDrawReason); + _dbg_assert_msg_(G3D, vfb, "DoSetRenderFramebuffer must return a valid framebuffer."); + _dbg_assert_msg_(G3D, currentRenderVfb_, "DoSetRenderFramebuffer must set a valid framebuffer."); return vfb; } } virtual void RebindFramebuffer() = 0; + std::vector GetFramebufferList(); void CopyDisplayToOutput(); @@ -210,8 +230,9 @@ public: bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); - virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) = 0; - virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) = 0; + virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h); + + virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader); void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); @@ -278,12 +299,13 @@ public: 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; - virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) = 0; - virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) = 0; - virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) = 0; + virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); + virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer); + virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer); + virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer); protected: + virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); virtual void SetViewport2D(int x, int y, int w, int h); void CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight, PostShaderUniforms *uniforms); virtual void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0; @@ -298,7 +320,7 @@ protected: void SetNumExtraFBOs(int num); virtual void DisableState() = 0; - virtual void FlushBeforeCopy() = 0; + void FlushBeforeCopy(); virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra // Used by ReadFramebufferToMemory and later framebuffer block copies @@ -343,6 +365,7 @@ protected: Draw::DrawContext *draw_ = nullptr; TextureCacheCommon *textureCache_ = nullptr; ShaderManagerCommon *shaderManager_ = nullptr; + DrawEngineCommon *drawEngine_ = nullptr; bool needBackBufferYSwap_ = false; u32 displayFramebufPtr_ = 0; @@ -391,6 +414,8 @@ protected: std::map tempFBOs_; + std::vector fbosToDelete_; + // Aggressively delete unused FBOs to save gpu memory. enum { FBO_OLD_AGE = 5, diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp new file mode 100644 index 0000000000..b38faffb47 --- /dev/null +++ b/GPU/Common/StencilCommon.cpp @@ -0,0 +1,36 @@ +#pragma once + +#include "GPU/Common/StencilCommon.h" + +u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + + for (u32 i = 0; i < numPixels / 2; ++i) { + if (ptr[i] & 0x80008000) { + return 1; + } + } + return 0; +} + +u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels / 2; ++i) { + bits |= ptr[i]; + } + + return ((bits >> 12) & 0xF) | (bits >> 28); +} + +u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels; ++i) { + bits |= ptr[i]; + } + + return bits >> 24; +} diff --git a/GPU/Common/StencilCommon.h b/GPU/Common/StencilCommon.h new file mode 100644 index 0000000000..ee26b4412e --- /dev/null +++ b/GPU/Common/StencilCommon.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Common/CommonTypes.h" + +u8 StencilBits8888(const u8 *ptr8, u32 numPixels); +u8 StencilBits4444(const u8 *ptr8, u32 numPixels); +u8 StencilBits5551(const u8 *ptr8, u32 numPixels); diff --git a/GPU/D3D11/DepalettizeShaderD3D11.cpp b/GPU/D3D11/DepalettizeShaderD3D11.cpp index 59c558f7df..1ab84dbd38 100644 --- a/GPU/D3D11/DepalettizeShaderD3D11.cpp +++ b/GPU/D3D11/DepalettizeShaderD3D11.cpp @@ -29,8 +29,6 @@ #include "GPU/D3D11/D3D11Util.h" #include "GPU/Common/DepalettizeShaderCommon.h" -static const int DEPAL_TEXTURE_OLD_AGE = 120; - #ifdef _WIN32 #define SHADERLOG #endif diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index ecf1ad3206..e8e6ba8580 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -786,10 +786,6 @@ rotateVBO: #endif } -bool DrawEngineD3D11::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { // Position if (prevSize < size) { diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 3a7d347932..7175a78e61 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -136,8 +136,6 @@ public: DecodeVerts(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - 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); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index f5d55de6f1..ee1e13e64d 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -133,22 +133,9 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw) ShaderTranslationInit(); CompilePostShader(); - - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = 512; // 512x512 is the maximum size of a framebuffer on the PSP. - packDesc.Height = 512; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTexture_)); } FramebufferManagerD3D11::~FramebufferManagerD3D11() { - packTexture_->Release(); ShaderTranslationShutdown(); // Drawing cleanup @@ -207,6 +194,11 @@ void FramebufferManagerD3D11::SetShaderManager(ShaderManagerD3D11 *sm) { shaderManager_ = sm; } +void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) { + drawEngineD3D11_ = td; + drawEngine_ = td; +} + void FramebufferManagerD3D11::DisableState() { context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); context_->RSSetState(stockD3D11.rasterStateNoCull); @@ -283,7 +275,7 @@ void FramebufferManagerD3D11::CompilePostShader() { } void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - u8 *convBuf = NULL; + u8 *convBuf = nullptr; // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { @@ -593,60 +585,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr } } -void FramebufferManagerD3D11::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - OptimizeDownloadRange(vfb, x, y, w, h); - if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { - // No need to blit - PackFramebufferD3D11_(vfb, x, y, w, h); - } - else { - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferD3D11_(nvfb, x, y, w, h); - } - - textureCacheD3D11_->ForgetLastTexture(); - RebindFramebuffer(); - } -} - -void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferD3D11_(nvfb, x, y, w, h); - - textureCacheD3D11_->ForgetLastTexture(); - RebindFramebuffer(); - } - } -} - bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; @@ -762,95 +700,6 @@ void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, false); } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_5551: // ABGR 1555 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_4444: // ABGR 4444 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - -// This function takes an already correctly-sized framebuffer and packs it into RAM. -// Does not need to account for scaling. -// Color conversion is currently done on CPU but should be done on GPU. -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"); - return; - } - - const u32 fb_address = (0x04000000) | vfb->fb_address; - const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; - - // We always need to convert from the framebuffer native format. - // Right now that's always 8888. - DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); - ID3D11Texture2D *colorTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT, 0); - - // TODO: Only copy the necessary rectangle. - D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x+w), (UINT)(y+h), 1 }; - context_->CopySubresourceRegion(packTexture_, 0, x, y, 0, colorTex, 0, &srcBox); - - // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game - // does a once-off copy, that won't work at all. - - // BIG GPU STALL - D3D11_MAPPED_SUBRESOURCE map; - HRESULT result = context_->Map(packTexture_, 0, D3D11_MAP_READ, 0, &map); - if (FAILED(result)) { - return; - } - - // TODO: Handle the other formats? We don't currently create them, I think. - const int srcByteOffset = y * map.RowPitch + x * 4; - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; - // Pixel size always 4 here because we always request BGRA8888. - ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)map.pData + srcByteOffset, vfb->fb_stride, map.RowPitch/4, w, h, vfb->format); - context_->Unmap(packTexture_, 0); -} - // Nobody calls this yet. void FramebufferManagerD3D11::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { @@ -869,30 +718,11 @@ void FramebufferManagerD3D11::DeviceLost() { DestroyAllFBOs(); } -std::vector FramebufferManagerD3D11::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerD3D11::DestroyAllFBOs() { - currentRenderVfb_ = 0; - displayFramebuf_ = 0; - prevDisplayFramebuf_ = 0; - prevPrevDisplayFramebuf_ = 0; + currentRenderVfb_ = nullptr; + displayFramebuf_ = nullptr; + prevDisplayFramebuf_ = nullptr; + prevPrevDisplayFramebuf_ = nullptr; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; @@ -915,17 +745,6 @@ void FramebufferManagerD3D11::DestroyAllFBOs() { DisableState(); } -void FramebufferManagerD3D11::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // 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(); -} - void FramebufferManagerD3D11::Resized() { FramebufferManagerCommon::Resized(); @@ -936,199 +755,3 @@ void FramebufferManagerD3D11::Resized() { // Might have a new post shader - let's compile it. CompilePostShader(); } - -// Lots of this code could be shared (like the downsampling). -bool FramebufferManagerD3D11::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - int w = vfb->renderWidth, h = vfb->renderHeight; - Draw::Framebuffer *fboForRead = nullptr; - if (vfb->fbo) { - if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { - w = vfb->width * maxRes; - h = vfb->height * maxRes; - - Draw::Framebuffer *tempFBO = GetTempFBO(w, h); - VirtualFramebuffer tempVfb = *vfb; - tempVfb.fbo = tempFBO; - tempVfb.bufferWidth = vfb->width; - tempVfb.bufferHeight = vfb->height; - tempVfb.renderWidth = w; - tempVfb.renderHeight = h; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - - fboForRead = tempFBO; - } else { - fboForRead = vfb->fbo; - } - } - if (!fboForRead) - return false; - - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - ID3D11Texture2D *nativeTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_COLOR_BIT, 0); - context_->CopyResource(packTex, nativeTex); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - uint8_t *dest = (uint8_t *)buffer.GetData() + y * w * 4; - const uint8_t *src = ((const uint8_t *)map.pData) + map.RowPitch * y; - memcpy(dest, src, 4 * w); - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; -} - -bool FramebufferManagerD3D11::GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil) { - int w = vfb->renderWidth, h = vfb->renderHeight; - Draw::Framebuffer *fboForRead = nullptr; - fboForRead = vfb->fbo; - - if (stencil) { - buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT); - } else if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { - buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256); - } else { - buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT); - } - - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = (DXGI_FORMAT)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_DEPTH_BIT | Draw::FB_FORMAT_BIT, 0); - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - ID3D11Texture2D *nativeTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_DEPTH_BIT, 0); - context_->CopyResource(packTex, nativeTex); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - float *dest = (float *)(buffer.GetData() + y * w * 4); - u8 *destStencil = buffer.GetData() + y * w; - const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); - for (int x = 0; x < w; x++) { - if (stencil) { - destStencil[x] = src[x] >> 24; - } else { - dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f); - } - } - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; -} - -bool FramebufferManagerD3D11::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - if (!vfb->fbo) { - return false; - } - - return GetDepthStencilBuffer(vfb, buffer, false); -} - -bool FramebufferManagerD3D11::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - return false; - } - - if (!vfb->fbo) { - return false; - } - - return GetDepthStencilBuffer(vfb, buffer, true); -} - -bool FramebufferManagerD3D11::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - ID3D11Texture2D *backbuffer = (ID3D11Texture2D *)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_COLOR_TEX); - if (!backbuffer) { - ERROR_LOG(G3D, "Failed to get backbuffer from draw context"); - return false; - } - D3D11_TEXTURE2D_DESC desc; - backbuffer->GetDesc(&desc); - int w = desc.Width; - int h = desc.Height; - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - context_->CopyResource(packTex, backbuffer); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - uint8_t *dest = (uint8_t *)buffer.GetData() + y * w * 4; - const uint8_t *src = ((const uint8_t *)map.pData) + map.RowPitch * y; - memcpy(dest, src, 4 * w); - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; -} diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 3e71678119..508e995e52 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -43,10 +43,7 @@ public: void SetTextureCache(TextureCacheD3D11 *tc); void SetShaderManager(ShaderManagerD3D11 *sm); - void SetDrawEngine(DrawEngineD3D11 *td) { - drawEngine_ = td; - } - + void SetDrawEngine(DrawEngineD3D11 *td); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void DestroyAllFBOs(); @@ -60,18 +57,8 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - - std::vector GetFramebufferList(); - virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; - bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; // TODO: Remove @@ -81,7 +68,6 @@ public: protected: void DisableState() override; - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -94,13 +80,11 @@ private: void BindPostShader(const PostShaderUniforms &uniforms) override; void Bind2DShader() override; void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SimpleBlit( Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, bool linearFilter); - bool GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil); ID3D11Device *device_; ID3D11DeviceContext *context_; @@ -133,11 +117,7 @@ private: TextureCacheD3D11 *textureCacheD3D11_; ShaderManagerD3D11 *shaderManagerD3D11_; - DrawEngineD3D11 *drawEngine_; - - // Permanent 1:1 readback texture, 512x512 fixed - // For larger debug readbacks, we create/destroy textures on the fly. - ID3D11Texture2D *packTexture_; + DrawEngineD3D11 *drawEngineD3D11_; // Used by post-processing shader // Postprocessing diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 6874d43759..cc3f06f16e 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -317,12 +317,13 @@ void GPU_D3D11::EndHostFrame() { } void GPU_D3D11::BeginFrameInternal() { + GPUCommon::BeginFrameInternal(); + textureCacheD3D11_->StartFrame(); drawEngine_.BeginFrame(); depalShaderCache_->Decimate(); // fragmentTestCache_.Decimate(); - GPUCommon::BeginFrameInternal(); shaderManagerD3D11_->DirtyLastShader(); framebufferManagerD3D11_->BeginFrame(); @@ -755,10 +756,6 @@ void GPU_D3D11::ClearShaderCache() { shaderManagerD3D11_->ClearShaders(); } -std::vector GPU_D3D11::GetFramebufferList() { - return framebufferManagerD3D11_->GetFramebufferList(); -} - void GPU_D3D11::DoState(PointerWrap &p) { GPUCommon::DoState(p); @@ -774,21 +771,6 @@ void GPU_D3D11::DoState(PointerWrap &p) { } } -bool GPU_D3D11::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - return textureCacheD3D11_->GetCurrentTextureDebug(buffer, level); -} - -bool GPU_D3D11::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheD3D11_->GetCurrentClutBuffer(buffer); -} - -bool GPU_D3D11::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - std::vector GPU_D3D11::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index 6785693bf0..c83bc35534 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -60,11 +60,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; - - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; typedef void (GPU_D3D11::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { diff --git a/GPU/D3D11/StencilBufferD3D11.cpp b/GPU/D3D11/StencilBufferD3D11.cpp index 539a9eaeb5..c8825d121b 100644 --- a/GPU/D3D11/StencilBufferD3D11.cpp +++ b/GPU/D3D11/StencilBufferD3D11.cpp @@ -21,6 +21,7 @@ #include "ext/native/thin3d/thin3d.h" #include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/D3D11/FramebufferManagerD3D11.h" #include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" #include "GPU/D3D11/ShaderManagerD3D11.h" @@ -71,39 +72,6 @@ VS_OUT main(VS_IN In) { } )"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - // TODO : If SV_StencilRef is available (D3D11.3) then this can be done in a single pass. bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 00500d8800..6423ad3307 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -516,9 +516,6 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry, bool replaceIma } } - // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. - DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - int scaleFactor = standardScaleFactor_; // Rachet down scale factor in low-memory mode. @@ -575,6 +572,8 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry, bool replaceIma maxLevel = 0; } + DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); + if (IsFakeMipmapChange()) { // NOTE: Since the level is not part of the cache key, we assume it never changes. u8 level = std::max(0, gstate.getTexLevelOffset16() / 16); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 1cd8d66f2e..68d5d837fb 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -707,10 +707,6 @@ rotateVBO: host->GPUNotifyDraw(); } -bool DrawEngineDX9::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { } diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 4c4f96acc3..58bbfb3888 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -133,8 +133,6 @@ public: DecodeVerts(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - 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); diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index d4e4aa1492..c5b319d950 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -155,6 +155,11 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { shaderManager_ = sm; } + void FramebufferManagerDX9::SetDrawEngine(DrawEngineDX9 *td) { + drawEngineD3D9_ = td; + drawEngine_ = td; + } + void FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { u8 *convBuf = NULL; D3DLOCKED_RECT rect; @@ -473,55 +478,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - OptimizeDownloadRange(vfb, x, y, w, h); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferDirectx9_(nvfb, x, y, w, h); - - textureCacheDX9_->ForgetLastTexture(); - RebindFramebuffer(); - } - } - - void FramebufferManagerDX9::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferDirectx9_(nvfb, x, y, w, h); - - textureCacheDX9_->ForgetLastTexture(); - RebindFramebuffer(); - } - } - } - bool FramebufferManagerDX9::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; @@ -640,7 +596,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + void FramebufferManagerDX9::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0"); return; @@ -737,25 +693,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { DestroyAllFBOs(); } - std::vector FramebufferManagerDX9::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; - } - void FramebufferManagerDX9::DecimateFBOs() { FramebufferManagerCommon::DecimateFBOs(); for (auto it = offscreenSurfaces_.begin(); it != offscreenSurfaces_.end(); ) { @@ -800,17 +737,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { DisableState(); } - void FramebufferManagerDX9::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // 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(); - } - void FramebufferManagerDX9::Resized() { FramebufferManagerCommon::Resized(); diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index f8960a4690..544438ed39 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -45,10 +45,7 @@ public: void SetTextureCache(TextureCacheDX9 *tc); void SetShaderManager(ShaderManagerDX9 *sm); - void SetDrawEngine(DrawEngineDX9 *td) { - drawEngine_ = td; - } - + void SetDrawEngine(DrawEngineDX9 *td); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void DestroyAllFBOs(); @@ -62,11 +59,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - - std::vector GetFramebufferList(); - virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); @@ -84,7 +76,6 @@ protected: void BindPostShader(const PostShaderUniforms &uniforms) override; void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override; - void FlushBeforeCopy() override; void DecimateFBOs() override; // Used by ReadFramebufferToMemory and later framebuffer block copies @@ -95,7 +86,7 @@ protected: private: void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer); @@ -120,7 +111,7 @@ private: TextureCacheDX9 *textureCacheDX9_; ShaderManagerDX9 *shaderManagerDX9_; - DrawEngineDX9 *drawEngine_; + DrawEngineDX9 *drawEngineD3D9_; struct TempFBO { Draw::Framebuffer *fbo; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 48c51cfe97..0dd93e7137 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -295,7 +295,7 @@ void GPU_DX9::BeginFrameInternal() { GPUCommon::BeginFrameInternal(); shaderManagerDX9_->DirtyShader(); - framebufferManagerDX9_->BeginFrame(); + framebufferManager_->BeginFrame(); } void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { @@ -688,10 +688,6 @@ void GPU_DX9::ClearShaderCache() { shaderManagerDX9_->ClearCache(true); } -std::vector GPU_DX9::GetFramebufferList() { - return framebufferManagerDX9_->GetFramebufferList(); -} - void GPU_DX9::DoState(PointerWrap &p) { GPUCommon::DoState(p); @@ -707,103 +703,6 @@ void GPU_DX9::DoState(PointerWrap &p) { } } -bool GPU_DX9::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - - textureCacheDX9_->SetTexture(true); - textureCacheDX9_->ApplyTexture(); - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); - - LPDIRECT3DBASETEXTURE9 baseTex; - LPDIRECT3DTEXTURE9 tex; - LPDIRECT3DSURFACE9 offscreen = nullptr; - HRESULT hr; - - bool success = false; - hr = device_->GetTexture(0, &baseTex); - if (SUCCEEDED(hr) && baseTex != NULL) { - hr = baseTex->QueryInterface(IID_IDirect3DTexture9, (void **)&tex); - if (SUCCEEDED(hr)) { - D3DSURFACE_DESC desc; - D3DLOCKED_RECT locked; - tex->GetLevelDesc(level, &desc); - RECT rect = {0, 0, (LONG)desc.Width, (LONG)desc.Height}; - hr = tex->LockRect(level, &locked, &rect, D3DLOCK_READONLY); - - // If it fails, this means it's a render-to-texture, so we have to get creative. - if (FAILED(hr)) { - LPDIRECT3DSURFACE9 renderTarget = nullptr; - hr = tex->GetSurfaceLevel(level, &renderTarget); - if (renderTarget && SUCCEEDED(hr)) { - hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, NULL); - if (SUCCEEDED(hr)) { - hr = device_->GetRenderTargetData(renderTarget, offscreen); - if (SUCCEEDED(hr)) { - hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY); - } - } - renderTarget->Release(); - } - } - - if (SUCCEEDED(hr)) { - GPUDebugBufferFormat fmt; - int pixelSize; - switch (desc.Format) { - case D3DFMT_A1R5G5B5: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_5551 : GPU_DBG_FORMAT_5551_BGRA; - pixelSize = 2; - break; - case D3DFMT_A4R4G4B4: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_4444 : GPU_DBG_FORMAT_4444_BGRA; - pixelSize = 2; - break; - case D3DFMT_R5G6B5: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_565 : GPU_DBG_FORMAT_565_BGRA; - pixelSize = 2; - break; - case D3DFMT_A8R8G8B8: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_8888_BGRA; - pixelSize = 4; - break; - default: - fmt = GPU_DBG_FORMAT_INVALID; - break; - } - - if (fmt != GPU_DBG_FORMAT_INVALID) { - buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false); - memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height); - success = true; - } else { - success = false; - } - if (offscreen) { - offscreen->UnlockRect(); - offscreen->Release(); - } else { - tex->UnlockRect(level); - } - } - tex->Release(); - } - baseTex->Release(); - } - - return success; -} - -bool GPU_DX9::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheDX9_->GetCurrentClutBuffer(buffer); -} - -bool GPU_DX9::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - std::vector GPU_DX9::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 261e21f700..72e76267ff 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -61,11 +61,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; - - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; typedef void (GPU_DX9::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { diff --git a/GPU/Directx9/StencilBufferDX9.cpp b/GPU/Directx9/StencilBufferDX9.cpp index b667abe58c..8d8e91b610 100644 --- a/GPU/Directx9/StencilBufferDX9.cpp +++ b/GPU/Directx9/StencilBufferDX9.cpp @@ -22,6 +22,7 @@ #include "gfx/d3d9_state.h" #include "ext/native/thin3d/thin3d.h" #include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/Directx9/FramebufferDX9.h" #include "GPU/Directx9/PixelShaderGeneratorDX9.h" #include "GPU/Directx9/ShaderManagerDX9.h" @@ -63,40 +64,6 @@ static const char *stencil_vs = " return Out;\n" "}\n"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { return false; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 889322ac04..76abb70d66 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -793,4 +793,89 @@ bool TextureCacheDX9::DecodeTexture(u8 *output, const GPUgstate &state) return true; } +bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { + SetTexture(true); + ApplyTexture(); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + + LPDIRECT3DBASETEXTURE9 baseTex; + LPDIRECT3DTEXTURE9 tex; + LPDIRECT3DSURFACE9 offscreen = nullptr; + HRESULT hr; + + bool success = false; + hr = device_->GetTexture(0, &baseTex); + if (SUCCEEDED(hr) && baseTex != NULL) { + hr = baseTex->QueryInterface(IID_IDirect3DTexture9, (void **)&tex); + if (SUCCEEDED(hr)) { + D3DSURFACE_DESC desc; + D3DLOCKED_RECT locked; + tex->GetLevelDesc(level, &desc); + RECT rect = { 0, 0, (LONG)desc.Width, (LONG)desc.Height }; + hr = tex->LockRect(level, &locked, &rect, D3DLOCK_READONLY); + + // If it fails, this means it's a render-to-texture, so we have to get creative. + if (FAILED(hr)) { + LPDIRECT3DSURFACE9 renderTarget = nullptr; + hr = tex->GetSurfaceLevel(level, &renderTarget); + if (renderTarget && SUCCEEDED(hr)) { + hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, NULL); + if (SUCCEEDED(hr)) { + hr = device_->GetRenderTargetData(renderTarget, offscreen); + if (SUCCEEDED(hr)) { + hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY); + } + } + renderTarget->Release(); + } + } + + if (SUCCEEDED(hr)) { + GPUDebugBufferFormat fmt; + int pixelSize; + switch (desc.Format) { + case D3DFMT_A1R5G5B5: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_5551 : GPU_DBG_FORMAT_5551_BGRA; + pixelSize = 2; + break; + case D3DFMT_A4R4G4B4: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_4444 : GPU_DBG_FORMAT_4444_BGRA; + pixelSize = 2; + break; + case D3DFMT_R5G6B5: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_565 : GPU_DBG_FORMAT_565_BGRA; + pixelSize = 2; + break; + case D3DFMT_A8R8G8B8: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_8888_BGRA; + pixelSize = 4; + break; + default: + fmt = GPU_DBG_FORMAT_INVALID; + break; + } + + if (fmt != GPU_DBG_FORMAT_INVALID) { + buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false); + memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height); + success = true; + } else { + success = false; + } + if (offscreen) { + offscreen->UnlockRect(); + offscreen->Release(); + } else { + tex->UnlockRect(level); + } + } + tex->Release(); + } + baseTex->Release(); + } + + return success; +} + }; diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index ed8a406852..8e4962bd3a 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -57,6 +57,8 @@ public: void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; + protected: void BindTexture(TexCacheEntry *entry) override; void Unbind() override; diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index c5fac70801..4670584cb0 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -118,8 +118,6 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l bool hasTexcoordTess = id.Bit(VS_BIT_HAS_TEXCOORD_TESS); bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS); - bool scaleUV = !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN); - DoLightComputation doLight[4] = { LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF }; if (useHWTransform) { int shadeLight0 = doShadeMapping ? ls0 : -1; @@ -766,6 +764,9 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l WRITE(p, " Out.v_color1 = float3(0, 0, 0);\n"); } + bool scaleUV = !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN); + + // Step 3: UV generation if (doTexture) { switch (uvGenMode) { diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 299f0eb915..1b30ce443e 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -26,8 +26,6 @@ #include "ext/native/gfx/GLStateCache.h" #include "GPU/Common/DepalettizeShaderCommon.h" -static const int DEPAL_TEXTURE_OLD_AGE = 120; - #ifdef _WIN32 #define SHADERLOG #endif diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 273dc1a2c7..0cc54fbb85 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -46,8 +46,6 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "GPU/GLES/ShaderManagerGLES.h" -// #define DEBUG_READ_PIXELS 1 - static const char tex_fs[] = "#if __VERSION__ >= 130\n" "#define varying in\n" @@ -241,6 +239,11 @@ void FramebufferManagerGLES::SetShaderManager(ShaderManagerGLES *sm) { shaderManager_ = sm; } +void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) { + drawEngineGL_ = td; + drawEngine_ = td; +} + FramebufferManagerGLES::~FramebufferManagerGLES() { if (drawPixelsTex_) glDeleteTextures(1, &drawPixelsTex_); @@ -419,8 +422,8 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float glEnableVertexAttribArray(program->a_position); glEnableVertexAttribArray(program->a_texcoord0); if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { - drawEngine_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords)); - drawEngine_->BindElementBuffer(indices, sizeof(indices)); + drawEngineGL_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords)); + drawEngineGL_->BindElementBuffer(indices, sizeof(indices)); glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0); glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos)); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); @@ -573,39 +576,7 @@ void FramebufferManagerGLES::DownloadFramebufferForClut(u32 fb_address, u32 load PROFILE_THIS_SCOPE("gpu-readback"); // Flush async just in case. PackFramebufferAsync_(nullptr); - - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheGL_->ForgetLastTexture(); - RebindFramebuffer(); - } - } + FramebufferManagerCommon::DownloadFramebufferForClut(fb_address, loadBytes); } bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { @@ -831,60 +802,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u } } -#ifdef DEBUG_READ_PIXELS -// TODO: Make more generic. -static void LogReadPixelsError(GLenum error) { - switch (error) { - case GL_NO_ERROR: - break; - case GL_INVALID_ENUM: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); - break; -#ifndef USING_GLES2 - case GL_STACK_UNDERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); - break; - case GL_STACK_OVERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); - break; -#endif - default: - ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); - break; - } -} -#endif - -static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, GLenum type, void *pixels) { - CHECK_GL_ERROR_IF_DEBUG(); - if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { - // Some drivers seem to require we specify this. See #8254. - glPixelStorei(GL_PACK_ROW_LENGTH, w); - } - - glReadPixels(x, y, w, h, fmt, type, pixels); -#ifdef DEBUG_READ_PIXELS - LogReadPixelsError(glGetError()); -#endif - - if (!gl_extensions.IsGLES || gl_extensions.GLES3) { - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - } - CHECK_GL_ERROR_IF_DEBUG(); -} - void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; @@ -944,59 +861,36 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { // Order packing/readback of the framebuffer if (vfb) { - int pixelType, pixelSize, pixelFormat, align; - bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER); + Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED; switch (vfb->format) { - // GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC) - // GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value) - // We want R G B A, so we use *_REV for 16-bit formats and GL_UNSIGNED_BYTE for 32-bit - case GE_FORMAT_4444: // 16 bit RGBA -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_4_4_4_4; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4); -#endif - pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_5551: // 16 bit RGBA -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_5_5_5_1; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1); -#endif - pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_565: // 16 bit RGB -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_5_6_5; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5); -#endif - pixelFormat = GL_RGB; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_8888: // 32 bit RGBA - default: - pixelType = GL_UNSIGNED_BYTE; - pixelFormat = UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA; - pixelSize = 4; - align = 4; - break; + case GE_FORMAT_4444: // 16 bit RGBA + dataFmt = (reverseOrder ? Draw::DataFormat::A4R4G4B4_UNORM_PACK16 : Draw::DataFormat::B4G4R4A4_UNORM_PACK16); + break; + case GE_FORMAT_5551: // 16 bit RGBA + dataFmt = (reverseOrder ? Draw::DataFormat::A1R5G5B5_UNORM_PACK16 : Draw::DataFormat::B5G5R5A1_UNORM_PACK16); + break; + case GE_FORMAT_565: // 16 bit RGB + dataFmt = (reverseOrder ? Draw::DataFormat::R5G6B5_UNORM_PACK16 : Draw::DataFormat::B5G6R5_UNORM_PACK16); + break; + case GE_FORMAT_8888: // 32 bit RGBA + dataFmt = UseBGRA8888() ? Draw::DataFormat::B8G8R8A8_UNORM : Draw::DataFormat::R8G8B8A8_UNORM; + break; + }; + + if (useCPU) { + dataFmt = UseBGRA8888() ? Draw::DataFormat::B8G8R8A8_UNORM : Draw::DataFormat::R8G8B8A8_UNORM; } + + int pixelSize = (int)DataFormatSizeInBytes(dataFmt); + int align = pixelSize; + // If using the CPU, we need 4 bytes per pixel always. - u32 bufSize = vfb->fb_stride * vfb->height * (useCPU ? 4 : pixelSize); + u32 bufSize = vfb->fb_stride * vfb->height * pixelSize; u32 fb_address = (0x04000000) | vfb->fb_address; - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0"); return; } @@ -1009,15 +903,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelBufObj_[currentPBO_].maxSize = bufSize; } - if (useCPU) { - // If converting pixel formats on the CPU we'll always request RGBA8888 - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, 0); - } else { - // Otherwise we'll directly request the format we need and let the GPU sort it out - glPixelStorei(GL_PACK_ALIGNMENT, align); - SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); - } + // TODO: This is a hack since PBOs have not been implemented in Thin3D yet(and maybe shouldn't? maybe should do this internally?) + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr, vfb->fb_stride); unbind = true; @@ -1038,9 +925,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); return; } @@ -1061,9 +946,9 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x int dstByteOffset = y * vfb->fb_stride * dstBpp; u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); - GLubyte *packed = nullptr; + u8 *packed = nullptr; if (!convert) { - packed = (GLubyte *)dst; + packed = (u8 *)dst; } else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address if (!convBuf_ || convBufSize_ < bufSize) { @@ -1077,20 +962,15 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (packed) { DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - GLenum glfmt = GL_RGBA; - if (UseBGRA8888()) { - glfmt = GL_BGRA_EXT; - } - CHECK_GL_ERROR_IF_DEBUG(); - - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed); + int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, UseBGRA8888() ? Draw::DataFormat::B8G8R8A8_UNORM : Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); if (convert) { ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); } } + // TODO: Move this into Thin3d. if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { #ifdef USING_GLES2 // GLES3 doesn't support using GL_READ_FRAMEBUFFER here. @@ -1106,9 +986,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x } void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); return; } @@ -1126,8 +1004,8 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->z_stride, h, GL_DEPTH_COMPONENT, GL_FLOAT, convBuf_); + int packW = h == 1 ? packWidth : vfb->z_stride; + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, y, packW, h, Draw::DataFormat::D32F, convBuf_, packW); int dstByteOffset = y * vfb->fb_stride * sizeof(u16); u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset); @@ -1176,25 +1054,6 @@ void FramebufferManagerGLES::DeviceLost() { DestroyDraw2DProgram(); } -std::vector FramebufferManagerGLES::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerGLES::DestroyAllFBOs() { CHECK_GL_ERROR_IF_DEBUG(); currentRenderVfb_ = 0; @@ -1224,18 +1083,6 @@ void FramebufferManagerGLES::DestroyAllFBOs() { CHECK_GL_ERROR_IF_DEBUG(); } -void FramebufferManagerGLES::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // 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(); - CHECK_GL_ERROR_IF_DEBUG(); -} - void FramebufferManagerGLES::Resized() { FramebufferManagerCommon::Resized(); @@ -1251,119 +1098,10 @@ void FramebufferManagerGLES::Resized() { } } -bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - int w = vfb->renderWidth, h = vfb->renderHeight; - if (vfb->fbo) { - if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { - w = vfb->width * maxRes; - h = vfb->height * maxRes; - - Draw::Framebuffer *tempFBO = GetTempFBO(w, h); - VirtualFramebuffer tempVfb = *vfb; - tempVfb.fbo = tempFBO; - tempVfb.bufferWidth = vfb->width; - tempVfb.bufferHeight = vfb->height; - tempVfb.renderWidth = w; - tempVfb.renderHeight = h; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - - draw_->BindFramebufferForRead(tempFBO); - } else { - draw_->BindFramebufferForRead(vfb->fbo); - } - } - - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); - - // We may have blitted to a temp FBO. - RebindFramebuffer(); - CHECK_GL_ERROR_IF_DEBUG(); - return true; -} - bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - int pw = PSP_CoreParameter().pixelWidth; - int ph = PSP_CoreParameter().pixelHeight; - - // The backbuffer is flipped. - buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); - CHECK_GL_ERROR_IF_DEBUG(); + int w, h; + draw_->GetFramebufferDimensions(nullptr, &w, &h); + buffer.Allocate(w, h, GPU_DBG_FORMAT_888_RGB, true); + draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData(), w); return true; } - -bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - if (!vfb->fbo) { - return false; - } - - if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT_DIV_256, !useBufferedRendering_); - } else { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); - } - if (vfb->fbo) - draw_->BindFramebufferForRead(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); - CHECK_GL_ERROR_IF_DEBUG(); - return true; -} - -bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - // TODO: Actually get the stencil. - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); - return true; - } - -#ifndef USING_GLES2 - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); - if (vfb->fbo) - draw_->BindFramebufferForRead(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - glPixelStorei(GL_PACK_ALIGNMENT, 2); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); - CHECK_GL_ERROR_IF_DEBUG(); - return true; -#else - return false; -#endif -} diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 7c43c11b24..f867f833d6 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -55,9 +55,7 @@ public: void SetTextureCache(TextureCacheGLES *tc); void SetShaderManager(ShaderManagerGLES *sm); - void SetDrawEngine(DrawEngineGLES *td) { - drawEngine_ = td; - } + void SetDrawEngine(DrawEngineGLES *td); // x,y,w,h are relative to destW, destH which fill out the target completely. void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; @@ -80,13 +78,8 @@ public: void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - std::vector GetFramebufferList(); - bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; virtual void RebindFramebuffer() override; @@ -94,7 +87,6 @@ public: protected: void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override; - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -111,7 +103,7 @@ private: void CompilePostShader(); void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); // Used by DrawPixels @@ -134,7 +126,7 @@ private: TextureCacheGLES *textureCacheGL_; ShaderManagerGLES *shaderManagerGL_; - DrawEngineGLES *drawEngine_; + DrawEngineGLES *drawEngineGL_; // Not used under ES currently. AsyncPBO *pixelBufObj_; //this isn't that large diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index a36450a494..b7d25debd3 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -929,10 +929,6 @@ void GPU_GLES::CleanupBeforeUI() { glstate.elementArrayBuffer.bind(0); } -std::vector GPU_GLES::GetFramebufferList() { - return framebufferManagerGL_->GetFramebufferList(); -} - void GPU_GLES::DoState(PointerWrap &p) { GPUCommon::DoState(p); @@ -950,59 +946,6 @@ void GPU_GLES::DoState(PointerWrap &p) { } } -bool GPU_GLES::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - -#ifndef USING_GLES2 - GPUgstate saved; - if (level != 0) { - saved = gstate; - - // The way we set textures is a bit complex. Let's just override level 0. - gstate.texsize[0] = gstate.texsize[level]; - gstate.texaddr[0] = gstate.texaddr[level]; - gstate.texbufwidth[0] = gstate.texbufwidth[level]; - } - - textureCacheGL_->SetTexture(true); - textureCacheGL_->ApplyTexture(); - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - if (level != 0) { - gstate = saved; - } - - buffer.Allocate(w, h, GE_FORMAT_8888, false); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); - - return true; -#else - return false; -#endif -} - -bool GPU_GLES::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheGL_->GetCurrentClutBuffer(buffer); -} - -bool GPU_GLES::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - -bool GPU_GLES::DescribeCodePtr(const u8 *ptr, std::string &name) { - if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { - name = "VertexDecoderJit"; - return true; - } - return false; -} - std::vector GPU_GLES::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 8919bd0555..e15a26c4d8 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -63,13 +63,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; - - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; - - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; typedef void (GPU_GLES::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index d570214917..de04a7c207 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -18,6 +18,7 @@ #include "gfx_es2/glsl_program.h" #include "Core/Reporting.h" #include "ext/native/gfx/GLStateCache.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/GLES/FramebufferManagerGLES.h" #include "GPU/GLES/ShaderManagerGLES.h" #include "GPU/GLES/TextureCacheGLES.h" @@ -59,40 +60,6 @@ static const char *stencil_vs = " gl_Position = a_position;\n" "}\n"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { return false; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index bcd14c7099..08aa6c8ca0 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -1045,3 +1045,36 @@ bool TextureCacheGLES::DecodeTexture(u8* output, const GPUgstate &state) { gstate = oldState; return true; } + +bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { +#ifndef USING_GLES2 + GPUgstate saved; + if (level != 0) { + saved = gstate; + + // The way we set textures is a bit complex. Let's just override level 0. + gstate.texsize[0] = gstate.texsize[level]; + gstate.texaddr[0] = gstate.texaddr[level]; + gstate.texbufwidth[0] = gstate.texbufwidth[level]; + } + + SetTexture(true); + ApplyTexture(); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + + if (level != 0) { + gstate = saved; + } + + buffer.Allocate(w, h, GE_FORMAT_8888, false); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); + + return true; +#else + return false; +#endif +} diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index 0d8ec3ea9f..56e4b99539 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -75,6 +75,7 @@ public: bool DecodeTexture(u8 *output, const GPUgstate &state); void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; protected: void BindTexture(TexCacheEntry *entry) override; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index b53ade9739..9c68a6fee3 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -196,6 +196,7 @@ + true true @@ -282,6 +283,7 @@ + true true @@ -358,6 +360,7 @@ + @@ -366,6 +369,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index cbbd189fe9..6e85d8e48f 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -265,6 +265,9 @@ Debugger + + Common + @@ -525,5 +528,14 @@ Debugger + + Vulkan + + + Common + + + Common + \ No newline at end of file diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 47a120d834..2704375db6 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2509,6 +2509,29 @@ bool GPUCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return framebufferManager_ ? framebufferManager_->GetOutputFramebuffer(buffer) : false; } +std::vector GPUCommon::GetFramebufferList() { + return framebufferManager_->GetFramebufferList(); +} + +bool GPUCommon::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { + return drawEngineCommon_->GetCurrentSimpleVertices(count, vertices, indices); +} + +bool GPUCommon::GetCurrentClut(GPUDebugBuffer &buffer) { + return textureCache_->GetCurrentClutBuffer(buffer); +} + bool GPUCommon::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { + if (!gstate.isTextureMapEnabled()) { + return false; + } return textureCache_->GetCurrentTextureDebug(buffer, level); } + +bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) { + if (drawEngineCommon_->IsCodePtrVertexDecoder(ptr)) { + name = "VertexDecoderJit"; + return true; + } + return false; +} diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 56c3068cc2..3d2430125d 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -169,18 +169,22 @@ public: #define new DBG_NEW #endif - bool DescribeCodePtr(const u8 *ptr, std::string &name) override { - return false; - } - // From GPUDebugInterface. bool GetCurrentDisplayList(DisplayList &list) override; bool GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) override; bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer) override; bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer) override; bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; + bool GetCurrentClut(GPUDebugBuffer &buffer) override; + bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; + std::vector DebugGetShaderIDs(DebugShaderType shader) override { return std::vector(); }; + std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override { + return "N/A"; + } + bool DescribeCodePtr(const u8 *ptr, std::string &name) override; + std::vector ActiveDisplayLists() override; void ResetListPC(int listID, u32 pc) override; void ResetListStall(int listID, u32 stall) override; @@ -223,9 +227,7 @@ public: bool DecodeTexture(u8* dest, const GPUgstate &state) override { return false; } - std::vector GetFramebufferList() override { - return std::vector(); - } + std::vector GetFramebufferList() override; void ClearShaderCache() override {} void CleanupBeforeUI() override {} @@ -236,11 +238,6 @@ public: return -1; } - std::vector DebugGetShaderIDs(DebugShaderType shader) override { return std::vector(); }; - std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override { - return "N/A"; - } - typedef void (GPUCommon::*CmdFunc)(u32 op, u32 diff); protected: diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 0a08068a2b..812c164cb2 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -146,13 +146,6 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { if (!draw_) return; - float dstwidth = (float)PSP_CoreParameter().pixelWidth; - float dstheight = (float)PSP_CoreParameter().pixelHeight; - - Draw::Viewport viewport = {0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f}; - draw_->SetViewports(1, &viewport); - draw_->SetScissorRect(0, 0, dstwidth, dstheight); - float u0 = 0.0f; float u1; @@ -216,6 +209,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { fbTex = draw_->CreateTexture(desc); + float dstwidth = (float)PSP_CoreParameter().pixelWidth; + float dstheight = (float)PSP_CoreParameter().pixelHeight; + float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); @@ -242,6 +238,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { std::swap(v0, v1); } draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE }); + Draw::Viewport viewport = { 0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f }; + draw_->SetViewports(1, &viewport); + draw_->SetScissorRect(0, 0, dstwidth, dstheight); Draw::SamplerState *sampler; if (g_Config.iBufFilter == SCALE_NEAREST) { diff --git a/GPU/Source.cpp b/GPU/Source.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 2de073cabe..406afe614c 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -15,33 +15,153 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - +#include "Common/Vulkan/VulkanContext.h" +#include "GPU/GPUState.h" +#include "GPU/Common/DepalettizeShaderCommon.h" #include "GPU/Vulkan/DepalettizeShaderVulkan.h" +#include "GPU/Vulkan/VulkanUtil.h" +#include "Common/Vulkan/VulkanImage.h" -DepalShaderCacheVulkan::DepalShaderCacheVulkan() { +static const char depal_vs[] = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (location = 0) in vec3 a_position; +layout (location = 1) in vec2 a_texcoord0; +layout (location = 0) out vec2 v_texcoord0; +out gl_PerVertex { vec4 gl_Position; }; +void main() { + v_texcoord0 = a_texcoord0; + gl_Position = vec4(a_position, 1.0); +} +)"; +static const VkComponentMapping VULKAN_4444_SWIZZLE = { VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B }; +static const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }; +static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; +static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + +static VkFormat GetClutDestFormat(GEPaletteFormat format, VkComponentMapping *componentMapping) { + switch (format) { + case GE_CMODE_16BIT_ABGR4444: + *componentMapping = VULKAN_4444_SWIZZLE; + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GE_CMODE_16BIT_ABGR5551: + *componentMapping = VULKAN_1555_SWIZZLE; + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GE_CMODE_16BIT_BGR5650: + *componentMapping = VULKAN_565_SWIZZLE; + return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GE_CMODE_32BIT_ABGR8888: + *componentMapping = VULKAN_8888_SWIZZLE; + return VK_FORMAT_R8G8B8A8_UNORM; + } + return VK_FORMAT_UNDEFINED; +} + +DepalShaderCacheVulkan::DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) + : draw_(draw), vulkan_(vulkan) { + std::string errors; + vshader_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, depal_vs, &errors); + assert(vshader_ != VK_NULL_HANDLE); } DepalShaderCacheVulkan::~DepalShaderCacheVulkan() { - + Clear(); + vulkan_->Delete().QueueDeleteShaderModule(vshader_); } -DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { - return nullptr; +DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat) { + u32 id = GenerateShaderID(clutMode, pixelFormat); + + auto shader = cache_.find(id); + if (shader != cache_.end()) { + return shader->second; + } + + VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); + + char *buffer = new char[2048]; + GenerateDepalShader(buffer, pixelFormat, GLSL_VULKAN); + + std::string error; + VkShaderModule fshader = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, buffer, &error); + if (fshader == VK_NULL_HANDLE) { + Crash(); + delete[] buffer; + return nullptr; + } + + VkPipeline pipeline = vulkan2D_->GetPipeline(rp, vshader_, fshader); + // Can delete the shader module now that the pipeline has been created. + // Maybe don't even need to queue it.. + vulkan_->Delete().QueueDeleteShaderModule(fshader); + + DepalShaderVulkan *depal = new DepalShaderVulkan(); + depal->pipeline = pipeline; + depal->code = buffer; + cache_[id] = depal; + return depal; } -VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) { - return nullptr; +VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, u32 clutID, u32 *rawClut) { + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + const u32 realClutID = clutID ^ clutFormat; + + auto oldtex = texCache_.find(realClutID); + if (oldtex != texCache_.end()) { + oldtex->second->lastFrame = gpuStats.numFlips; + return oldtex->second->texture; + } + + VkComponentMapping componentMapping; + VkFormat destFormat = GetClutDestFormat(clutFormat, &componentMapping); + int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512; + + VkBuffer pushBuffer; + uint32_t pushOffset = push_->PushAligned(rawClut, 1024, 4, &pushBuffer); + + VulkanTexture *vktex = new VulkanTexture(vulkan_, alloc_); + if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &componentMapping)) { + Crash(); + } + vktex->UploadMip(cmd, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); + vktex->EndCreate(cmd); + + DepalTextureVulkan *tex = new DepalTextureVulkan(); + tex->texture = vktex; + tex->lastFrame = gpuStats.numFlips; + texCache_[realClutID] = tex; + return tex->texture; } -void DepalShaderCacheVulkan::Clear() {} +void DepalShaderCacheVulkan::Clear() { + for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) { + // Don't need to destroy the pipelines, they're handled by Vulkan2D. + delete shader->second; + } + cache_.clear(); -void DepalShaderCacheVulkan::Decimate() {} - -u32 DepalShaderCacheVulkan::GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { - return 0; + for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) { + delete tex->second->texture; + delete tex->second; + } + texCache_.clear(); } -bool DepalShaderCacheVulkan::CreateVertexShader() { - return false; +void DepalShaderCacheVulkan::Decimate() { + // We don't bother decimating the generated shaders, there are never very many of them. + for (auto tex = texCache_.begin(); tex != texCache_.end(); ) { + if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) { + delete tex->second->texture; + delete tex->second; + texCache_.erase(tex++); + } else { + ++tex; + } + } +} + +u32 DepalShaderCacheVulkan::GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat) { + return (clutMode & 0xFFFFFF) | (pixelFormat << 24); } diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.h b/GPU/Vulkan/DepalettizeShaderVulkan.h index 63faa1c8b6..4e6e119b84 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.h +++ b/GPU/Vulkan/DepalettizeShaderVulkan.h @@ -20,42 +20,58 @@ #include #include "Common/CommonTypes.h" +#include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanMemory.h" #include "GPU/ge_constants.h" +#include "thin3d/thin3d.h" class DepalShaderVulkan { public: - /* - GLuint program; - GLuint fragShader; - GLint a_position; - GLint a_texcoord0; - */ + ~DepalShaderVulkan() { + delete[] code; + } + // A Vulkan2D pipeline. Set texture to slot 0 and palette texture to slot 1. + VkPipeline pipeline = VK_NULL_HANDLE; + const char *code = nullptr;; }; class DepalTextureVulkan { public: - int texture; + VulkanTexture *texture = nullptr; int lastFrame; }; class VulkanTexture; +class Vulkan2D; // Caches both shaders and palette textures. // Could even avoid bothering with palette texture and just use uniform data... class DepalShaderCacheVulkan { public: - DepalShaderCacheVulkan(); + DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan); ~DepalShaderCacheVulkan(); // This also uploads the palette and binds the correct texture. - DepalShaderVulkan *GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); + DepalShaderVulkan *GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat); VulkanTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut); void Clear(); void Decimate(); + void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; } + void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } + void SetAllocator(VulkanDeviceAllocator *alloc) { alloc_ = alloc; } + void SetVShader(VkShaderModule vshader) { vshader_ = vshader; } + private: - u32 GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); - bool CreateVertexShader(); + u32 GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat); + + Draw::DrawContext *draw_ = nullptr; + VulkanContext *vulkan_ = nullptr; + VulkanPushBuffer *push_ = nullptr; + VulkanDeviceAllocator *alloc_ = nullptr; + VkShaderModule vshader_ = VK_NULL_HANDLE; + Vulkan2D *vulkan2D_ = nullptr; // GLuint vertexShader_; std::map cache_; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0d9a0093a6..3fc9bd9026 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -21,6 +21,7 @@ #include "base/timeutil.h" #include "math/dataconv.h" #include "profiler/profiler.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/MemoryUtil.h" #include "Core/MemMap.h" @@ -56,7 +57,7 @@ enum { }; #define VERTEXCACHE_DECIMATION_INTERVAL 17 -#define DESCRIPTORSET_DECIMATION_INTERVAL 13 +#define DESCRIPTORSET_DECIMATION_INTERVAL 1 // Temporarily cut to 1. Handle reuse breaks this when textures get deleted. enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; @@ -78,7 +79,6 @@ enum { DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) : vulkan_(vulkan), draw_(draw), - curFrame_(0), stats_{}, vai_(1024) { decOptions_.expandAllWeightsToFloat = false; @@ -94,7 +94,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra InitDeviceObjects(); - tessDataTransfer = new TessellationDataTransferVulkan(vulkan); + tessDataTransfer = new TessellationDataTransferVulkan(vulkan, draw); } void DrawEngineVulkan::InitDeviceObjects() { @@ -125,7 +125,8 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[4].binding = DRAW_BINDING_DYNUBO_BONE; - // Hardware tessellation + // Hardware tessellation. TODO: Don't allocate these unless actually drawing splines. + // Will require additional bindings[5].descriptorCount = 1; bindings[5].pImmutableSamplers = nullptr; bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -151,9 +152,9 @@ void DrawEngineVulkan::InitDeviceObjects() { assert(VK_SUCCESS == res); VkDescriptorPoolSize dpTypes[2]; - dpTypes[0].descriptorCount = 4096; + dpTypes[0].descriptorCount = 8192; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - dpTypes[1].descriptorCount = 2048; + dpTypes[1].descriptorCount = 8192 + 4096; // Due to the tess stuff, we need a LOT of these. Most will be empty... dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; @@ -200,7 +201,7 @@ void DrawEngineVulkan::InitDeviceObjects() { samp.flags = 0; samp.magFilter = VK_FILTER_NEAREST; samp.minFilter = VK_FILTER_NEAREST; - res = vkCreateSampler(device, &samp, nullptr, &depalSampler_); + res = vkCreateSampler(device, &samp, nullptr, &samplerSecondary_); res = vkCreateSampler(device, &samp, nullptr, &nullSampler_); assert(VK_SUCCESS == res); @@ -240,11 +241,11 @@ void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) { } void DrawEngineVulkan::DestroyDeviceObjects() { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].Destroy(vulkan_); } - if (depalSampler_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteSampler(depalSampler_); + if (samplerSecondary_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteSampler(samplerSecondary_); if (nullSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(nullSampler_); if (pipelineLayout_ != VK_NULL_HANDLE) @@ -275,10 +276,10 @@ void DrawEngineVulkan::DeviceRestore(VulkanContext *vulkan) { } void DrawEngineVulkan::BeginFrame() { - lastCmd_ = VK_NULL_HANDLE; lastPipeline_ = nullptr; - FrameData *frame = &frame_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData *frame = &frame_[curFrame]; // First reset all buffers, then begin. This is so that Reset can free memory and Begin can allocate it, // if growing the buffer is needed. Doing it this way will reduce fragmentation if more than one buffer @@ -294,10 +295,12 @@ void DrawEngineVulkan::BeginFrame() { // TODO : Find a better place to do this. if (!nullTexture_) { + ILOG("INIT : Creating null texture"); + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); nullTexture_ = new VulkanTexture(vulkan_); int w = 8; int h = 8; - nullTexture_->CreateDirect(w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); uint32_t bindOffset; VkBuffer bindBuf; @@ -308,8 +311,8 @@ void DrawEngineVulkan::BeginFrame() { data[y*w + x] = 0; // black } } - nullTexture_->UploadMip(0, w, h, bindBuf, bindOffset, w); - nullTexture_->EndCreate(); + nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); + nullTexture_->EndCreate(cmdInit); } DirtyAllUBOs(); @@ -355,16 +358,13 @@ void DrawEngineVulkan::BeginFrame() { } void DrawEngineVulkan::EndFrame() { - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; stats_.pushUBOSpaceUsed = (int)frame->pushUBO->GetOffset(); stats_.pushVertexSpaceUsed = (int)frame->pushVertex->GetOffset(); stats_.pushIndexSpaceUsed = (int)frame->pushIndex->GetOffset(); frame->pushUBO->End(); frame->pushVertex->End(); frame->pushIndex->End(); - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) - curFrame_ = 0; vertexCache_->End(); } @@ -505,12 +505,11 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, } } - VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) { DescriptorSetKey key; key.imageView_ = imageView; key.sampler_ = sampler; - key.secondaryImageView_ = VK_NULL_HANDLE; + key.secondaryImageView_ = boundSecondary_; key.base_ = base; key.light_ = light; key.bone_ = bone; @@ -518,7 +517,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView assert(light != VK_NULL_HANDLE); assert(bone != VK_NULL_HANDLE); - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; if (!gstate_c.bezier && !gstate_c.spline) { // Has no cache when HW tessellation. VkDescriptorSet d = frame->descSets.Get(key); if (d != VK_NULL_HANDLE) @@ -535,7 +534,8 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView descAlloc.descriptorPool = frame->descPool; descAlloc.descriptorSetCount = 1; VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc); - assert(result == VK_SUCCESS); + // Even in release mode, this is bad. + _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptors in pool. sz=%d", (int)frame->descSets.size()); // We just don't write to the slots we don't care about. VkWriteDescriptorSet writes[7]; @@ -558,6 +558,21 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView n++; } + if (boundSecondary_) { + // TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them? + tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tex.imageView = boundSecondary_; + tex.sampler = samplerSecondary_; + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].pNext = nullptr; + writes[n].dstBinding = DRAW_BINDING_2ND_TEXTURE; + writes[n].pImageInfo = &tex; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[n].dstSet = desc; + n++; + } + // Skipping 2nd texture for now. // Tessellation data textures @@ -643,19 +658,14 @@ void DrawEngineVulkan::DoFlush() { // TODO: Should be enough to update this once per frame? gpuStats.numTrackedVertexArrays = (int)vai_.size(); - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - if (cmd != lastCmd_) { - lastPipeline_ = nullptr; - lastCmd_ = cmd; - // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - } + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + // HACK: These two lines should only execute if we started on a new render pass. Can't tell from in here though... + lastPipeline_ = nullptr; + // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. + // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS); - if (!rp) - Crash(); - - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { @@ -870,23 +880,28 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; } + renderManager->BindPipeline(pipeline->pipeline); if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { + gstate_c.Dirty(DIRTY_BLEND_STATE); + } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -894,30 +909,22 @@ void DrawEngineVulkan::DoFlush() { UpdateUBOs(frame); VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); - { - PROFILE_THIS_SCOPE("vkdraw"); + PROFILE_THIS_SCOPE("renderman_q"); const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); int stride = dec_->GetDecVtxFmt().stride; - VkDeviceSize offsets[1] = { vbOffset }; if (useElements) { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); - // TODO (maybe): Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments. - // Not sure if actually worth it, binding buffers should be fast. - 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); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdDraw(cmd, vertexCount, 1, 0, 0); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } } else { @@ -971,24 +978,28 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + renderManager->BindPipeline(pipeline->pipeline); if (!pipeline) { // Already logged, let's bail out. return; } if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw. + if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { + gstate_c.Dirty(DIRTY_BLEND_STATE); + } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -1002,25 +1013,19 @@ void DrawEngineVulkan::DoFlush() { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - PROFILE_THIS_SCOPE("vkdrawsoft"); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); + PROFILE_THIS_SCOPE("renderman_q"); if (drawIndexed) { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); 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); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); } 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); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); } } 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. @@ -1078,18 +1083,16 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { } } -bool DrawEngineVulkan::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) { int rowPitch; + ILOG("INIT : Prep tess"); + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position if (prevSize < size) { prevSize = size; - data_tex[0]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[0]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } pos = (float *)data_tex[0]->Lock(0, &rowPitch); @@ -1098,7 +1101,7 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeTex < size) { prevSizeTex = size; - data_tex[1]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[1]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } tex = (float *)data_tex[1]->Lock(0, &rowPitch); } @@ -1108,19 +1111,20 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeCol < sizeColor) { prevSizeCol = sizeColor; - data_tex[2]->CreateDirect(sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[2]->CreateDirect(cmd, sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } col = (float *)data_tex[2]->Lock(0, &rowPitch); } void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position - data_tex[0]->Unlock(); + data_tex[0]->Unlock(cmd); // Texcoords if (hasTexCoords) - data_tex[1]->Unlock(); + data_tex[1]->Unlock(cmd); // Color - data_tex[2]->Unlock(); + data_tex[2]->Unlock(cmd); } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index aa40ebf98d..2322dfdc45 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -113,6 +113,8 @@ public: u8 flags = 0; }; +class VulkanRenderManager; + // Handles transform, lighting and drawing. class DrawEngineVulkan : public DrawEngineCommon { public: @@ -147,8 +149,6 @@ public: DoFlush(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - 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); @@ -163,8 +163,12 @@ public: void DirtyAllUBOs(); + void DirtyPipeline() { + lastPipeline_ = nullptr; + } + VulkanPushBuffer *GetPushBufferForTextureData() { - return frame_[curFrame_].pushUBO; + return frame_[vulkan_->GetCurFrame()].pushUBO; } const DrawEngineVulkanStats &GetStats() const { @@ -173,8 +177,9 @@ public: private: struct FrameData; - void ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef); + void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); + void ResetShaderBlending(); void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -193,10 +198,13 @@ private: // We use a single descriptor set layout for all PSP draws. VkDescriptorSetLayout descriptorSetLayout_; VkPipelineLayout pipelineLayout_; - VkCommandBuffer lastCmd_ = VK_NULL_HANDLE; VulkanPipeline *lastPipeline_; VkDescriptorSet lastDs_ = VK_NULL_HANDLE; + // Secondary texture for shader blending + VkImageView boundSecondary_ = VK_NULL_HANDLE; + VkSampler samplerSecondary_ = VK_NULL_HANDLE; + PrehashMap vai_; VulkanPushBuffer *vertexCache_; int decimationCounter_ = 0; @@ -225,7 +233,6 @@ private: }; GEPrimitiveType lastPrim_ = GE_PRIM_INVALID; - int curFrame_; FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES]; // Other @@ -234,16 +241,14 @@ private: TextureCacheVulkan *textureCache_ = nullptr; FramebufferManagerVulkan *framebufferManager_ = nullptr; - VkSampler depalSampler_; - // State cache uint64_t dirtyUniforms_; uint32_t baseUBOOffset; uint32_t lightUBOOffset; uint32_t boneUBOOffset; VkBuffer baseBuf, lightBuf, boneBuf; - VkImageView imageView; - VkSampler sampler; + VkImageView imageView = VK_NULL_HANDLE; + VkSampler sampler = VK_NULL_HANDLE; // Null texture VulkanTexture *nullTexture_ = nullptr; @@ -257,14 +262,15 @@ private: // Hardware tessellation class TessellationDataTransferVulkan : public TessellationDataTransfer { private: - VulkanContext *vulkan; + VulkanContext *vulkan_; + Draw::DrawContext *draw_; VulkanTexture *data_tex[3]; VkSampler sampler; public: - TessellationDataTransferVulkan(VulkanContext *vulkan) - : TessellationDataTransfer(), vulkan(vulkan), data_tex(), sampler() { + TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) + : TessellationDataTransfer(), vulkan_(vulkan), draw_(draw), data_tex(), sampler() { for (int i = 0; i < 3; i++) - data_tex[i] = new VulkanTexture(vulkan); + data_tex[i] = new VulkanTexture(vulkan_); CreateSampler(); } @@ -272,7 +278,7 @@ private: for (int i = 0; i < 3; i++) delete data_tex[i]; - vulkan->Delete().QueueDeleteSampler(sampler); + vulkan_->Delete().QueueDeleteSampler(sampler); } void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) override; @@ -303,7 +309,7 @@ private: samp.minLod = 0.0f; samp.mipLodBias = 0.0f; - VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &sampler); + VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &sampler); assert(res == VK_SUCCESS); } }; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 8eadfa48fa..6abc89637b 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -28,6 +28,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" #include "Common/Vulkan/VulkanImage.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/ColorConv.h" #include "Core/Host.h" #include "Core/MemMap.h" @@ -87,19 +88,18 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk convBufSize_(0), textureCacheVulkan_(nullptr), shaderManagerVulkan_(nullptr), - pixelBufObj_(nullptr), - currentPBO_(0), - curFrame_(0), pipelinePostShader_(VK_NULL_HANDLE), - vulkan2D_(vulkan) { + depalVulkan_(draw, vulkan) { InitDeviceObjects(); + + // After a blit we do need to rebind for the VulkanRenderManager to know what to do. + needGLESRebinds_ = true; } FramebufferManagerVulkan::~FramebufferManagerVulkan() { delete[] convBuf_; - vulkan2D_.Shutdown(); DestroyDeviceObjects(); } @@ -113,24 +113,18 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) { shaderManager_ = sm; } +void FramebufferManagerVulkan::SetDrawEngine(DrawEngineVulkan *td) { + drawEngineVulkan_ = td; + drawEngine_ = td; +} + void FramebufferManagerVulkan::InitDeviceObjects() { - // Initialize framedata - for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); - } - - pipelineCache2D_ = vulkan_->CreatePipelineCache(); - std::string fs_errors, vs_errors; fsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, tex_fs, &fs_errors); vsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, tex_vs, &vs_errors); assert(fsBasicTex_ != VK_NULL_HANDLE); assert(vsBasicTex_ != VK_NULL_HANDLE); - // 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; samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; @@ -146,13 +140,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() { } void FramebufferManagerVulkan::DestroyDeviceObjects() { - for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - if (frameData_[i].push_) { - frameData_[i].push_->Destroy(vulkan_); - delete frameData_[i].push_; - frameData_[i].push_ = nullptr; - } - } delete drawPixelsTex_; drawPixelsTex_ = nullptr; @@ -160,41 +147,38 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() { vulkan_->Delete().QueueDeleteShaderModule(fsBasicTex_); if (vsBasicTex_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteShaderModule(vsBasicTex_); + if (stencilFs_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteShaderModule(stencilFs_); + if (stencilVs_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteShaderModule(stencilVs_); if (linearSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(linearSampler_); if (nearestSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(nearestSampler_); - // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. - if (pipelineCache2D_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeletePipelineCache(pipelineCache2D_); } void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) { - // if (!useBufferedRendering_) { - float x, y, w, h; - CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); + float x, y, w, h; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); - 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) - mask |= Draw::FBChannel::FB_COLOR_BIT; - if (clearDepth) - mask |= Draw::FBChannel::FB_DEPTH_BIT; - if (clearAlpha) - mask |= Draw::FBChannel::FB_STENCIL_BIT; + 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) + 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 { - // TODO: Clever render pass magic. - //} + draw_->Clear(mask, color, depth, 0); + if (clearColor || clearAlpha) { + SetColorUpdated(gstate_c.skipDrawReason); + } + if (clearDepth) { + SetDepthUpdated(); + } } void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) { @@ -226,22 +210,27 @@ void FramebufferManagerVulkan::Init() { } void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTex_->GetWidth() != width || drawPixelsTex_->GetHeight() != height)) { + if (drawPixelsTex_) { delete drawPixelsTex_; drawPixelsTex_ = nullptr; } - if (!drawPixelsTex_) { - drawPixelsTex_ = new VulkanTexture(vulkan_); - 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(); + VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + + drawPixelsTex_ = new VulkanTexture(vulkan_); + if (!drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) { + // out of memory? + delete drawPixelsTex_; + drawPixelsTex_ = nullptr; + overrideImageView_ = VK_NULL_HANDLE; + return; } + // Initialize backbuffer texture for DrawPixels + drawPixelsTexFormat_ = srcPixelFormat; // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. + // Could also convert directly into the pushbuffer easily. const uint8_t *data = srcPixels; if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) { u32 neededSize = width * height * 4; @@ -293,26 +282,23 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor } VkBuffer buffer; - size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer); - drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width); - drawPixelsTex_->EndCreate(); + size_t offset = push_->Push(data, width * height * 4, &buffer); + drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width); + drawPixelsTex_->EndCreate(initCmd); overrideImageView_ = drawPixelsTex_->GetImageView(); } void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) { - VkViewport vp; - vp.minDepth = 0.0; - vp.maxDepth = 1.0; - vp.x = (float)x; - vp.y = (float)y; - vp.width = (float)w; - vp.height = (float)h; - + Draw::Viewport vp; + vp.MinDepth = 0.0; + vp.MaxDepth = 1.0; + vp.TopLeftX = (float)x; + vp.TopLeftY = (float)y; + vp.Width = (float)w; + vp.Height = (float)h; // Since we're about to override it. - draw_->FlushState(); - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - vkCmdSetViewport(cmd, 0, 1, &vp); + draw_->SetViewports(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, int flags) { @@ -355,24 +341,21 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa // TODO: Should probably use draw_ directly and not go low level - VulkanPushBuffer *push = frameData_[curFrame_].push_; + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - - VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); if ((flags & DRAWTEX_KEEP_TEX) == 0) overrideImageView_ = VK_NULL_HANDLE; - vulkan2D_.BindDescriptorSet(cmd, view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_); + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; - VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset); - vkCmdDraw(cmd, 4, 1, 0, 0); + VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer); + renderManager->BindPipeline(cur2DPipeline_); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - cur2DPipeline_ = vulkan2D_.GetPipeline(pipelineCache2D_, rp, vsBasicTex_, fsBasicTex_); + cur2DPipeline_ = vulkan2D_->GetPipeline(rp, vsBasicTex_, fsBasicTex_); } void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) { @@ -387,12 +370,7 @@ void FramebufferManagerVulkan::RebindFramebuffer() { // Should this even happen? draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } -} - -bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { - // In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without - // messing about with bitplane textures and the like. - return false; + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); } int FramebufferManagerVulkan::GetLineWidth() { @@ -434,13 +412,15 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir 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; - - if (gstate_c.Supports(GPU_SUPPORTS_ANY_COPY_IMAGE) && matchingDepthBuffer && matchingRenderSize && matchingSize) { + if (matchingDepthBuffer && matchingRenderSize && matchingSize) { + // TODO: Currently, this copies depth AND stencil, which is a problem. See #9740. 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); draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST); + */ } } @@ -458,10 +438,6 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } // 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) { @@ -473,10 +449,10 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } else { draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); } - return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); } else if (framebuffer != currentRenderVfb_) { draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); - return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_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. @@ -484,174 +460,21 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } } -VulkanTexture *FramebufferManagerVulkan::GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags) { - if (framebuffer == NULL) { - framebuffer = currentRenderVfb_; - } - - if (!framebuffer->fbo || !useBufferedRendering_) { - gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; - return nullptr; - } - - // 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; - } - if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == fbRawAddress) { - // TODO: Enable the below code - return nullptr; // framebuffer->fbo->GetColor(); - /* - // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - VulkanFBO *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (FBOColorDepth)framebuffer->colorDepth); - if (renderCopy) { - VirtualFramebuffer copyInfo = *framebuffer; - copyInfo.fbo = renderCopy; - - int x = 0; - int y = 0; - int w = framebuffer->drawnWidth; - int h = framebuffer->drawnHeight; - - // If max is not > min, we probably could not detect it. Skip. - // See the vertex decoder, where this is updated. - if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) == BINDFBCOLOR_MAY_COPY_WITH_UV && gstate_c.vertBounds.maxU > gstate_c.vertBounds.minU) { - x = gstate_c.vertBounds.minU; - y = gstate_c.vertBounds.minV; - w = gstate_c.vertBounds.maxU - x; - h = gstate_c.vertBounds.maxV - y; - - // If we bound a framebuffer, apply the byte offset as pixels to the copy too. - if (flags & BINDFBCOLOR_APPLY_TEX_OFFSET) { - x += gstate_c.curTextureXOffset; - y += gstate_c.curTextureYOffset; - } - } - - BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0); - - return nullptr; // fbo_bind_color_as_texture(renderCopy, 0); - } else { - return framebuffer->fbo->GetColor(); - } - */ - } else { - return nullptr; // framebuffer->fbo->GetColor(); - } -} - -void FramebufferManagerVulkan::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - PROFILE_THIS_SCOPE("gpu-readback"); - if (sync) { - // flush async just in case when we go for synchronous update - // Doesn't actually pack when sent a null argument. - PackFramebufferAsync_(nullptr); - } - - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - OptimizeDownloadRange(vfb, x, y, w, h); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - // PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels - // PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs - - // TODO: Can we fall back to sync without these? - if (!sync) { - PackFramebufferAsync_(nvfb); - } else { - PackFramebufferSync_(nvfb, x, y, w, h); - } - - textureCacheVulkan_->ForgetLastTexture(); - RebindFramebuffer(); - } -} - -void FramebufferManagerVulkan::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - PROFILE_THIS_SCOPE("gpu-readback"); - // Flush async just in case. - PackFramebufferAsync_(nullptr); - - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheVulkan_->ForgetLastTexture(); - RebindFramebuffer(); - } - } -} - bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // When updating VRAM, it need to be exact format. - if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { - switch (nvfb->format) { - case GE_FORMAT_4444: - nvfb->colorDepth = VK_FBO_4444; - break; - case GE_FORMAT_5551: - nvfb->colorDepth = VK_FBO_5551; - break; - case GE_FORMAT_565: - nvfb->colorDepth = VK_FBO_565; - break; - case GE_FORMAT_8888: - default: - nvfb->colorDepth = VK_FBO_8888; - break; - } - } + nvfb->colorDepth = Draw::FBO_8888; - /* - nvfb->fbo = CreateFramebuffer(nvfb->width, nvfb->height, 1, false, (FBOColorDepth)nvfb->colorDepth); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; } - BindFramebufferAsRenderTarget(nvfb->fbo); - */ + draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); return true; } void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // _assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); - - // Discard the previous contents of this buffer where possible. - /* - if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { - BindFramebufferAsRenderTargetnvfb->fbo); - GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT }; - glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments); - } else if (gl_extensions.IsGLES) { - BindFramebufferAsRenderTargetnvfb->fbo); - } - */ + // Nothing to do here. } void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { @@ -756,205 +579,15 @@ void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcSt } } -#ifdef DEBUG_READ_PIXELS -// TODO: Make more generic. -static void LogReadPixelsError(GLenum error) { - switch (error) { - case GL_NO_ERROR: - break; - case GL_INVALID_ENUM: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); - break; -#ifndef USING_GLES2 - case GL_STACK_UNDERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); - break; - case GL_STACK_OVERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); - break; -#endif - default: - ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); - break; - } -} -#endif - -// One frame behind, but no stalling. -void FramebufferManagerVulkan::PackFramebufferAsync_(VirtualFramebuffer *vfb) { - const int MAX_PBO = 2; - uint8_t *packed = 0; - const u8 nextPBO = (currentPBO_ + 1) % MAX_PBO; - - bool useCPU = false; - - // We'll prepare two PBOs to switch between readying and reading - if (!pixelBufObj_) { - if (!vfb) { - // This call is just to flush the buffers. We don't have any yet, - // so there's nothing to do. - return; - } - - // GLuint pbos[MAX_PBO]; - // glGenBuffers(MAX_PBO, pbos); - - pixelBufObj_ = new AsyncPBOVulkan[MAX_PBO]; - for (int i = 0; i < MAX_PBO; i++) { - // TODO - // pixelBufObj_[i].handle = pbos[i]; - pixelBufObj_[i].maxSize = 0; - pixelBufObj_[i].reading = false; - } - } - - // Receive previously requested data from a PBO - AsyncPBOVulkan &pbo = pixelBufObj_[nextPBO]; - if (pbo.reading) { - // glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle); - // packed = (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pbo.size, GL_MAP_READ_BIT); - - if (packed) { - DEBUG_LOG(FRAMEBUF, "Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u", - pbo.size, packed, pbo.fb_address, pbo.stride, nextPBO); - - // We don't need to convert, GPU already did (or should have) - // (vulkan: hopefully) - Memory::MemcpyUnchecked(pbo.fb_address, packed, pbo.size); - - pbo.reading = false; - } - - // glUnmapBuffer(GL_PIXEL_PACK_BUFFER); - } - - // Order packing/readback of the framebuffer - if (vfb) { - // int pixelType, pixelFormat; - int pixelSize, align; - switch (vfb->format) { - case GE_FORMAT_4444: // 16 bit RGBA - // pixelType = GL_UNSIGNED_SHORT_4_4_4_4; - // pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_5551: // 16 bit RGBA - // pixelType = GL_UNSIGNED_SHORT_5_5_5_1; - // pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_565: // 16 bit RGB - // pixelType = GL_UNSIGNED_SHORT_5_6_5; - // pixelFormat = GL_RGB; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_8888: // 32 bit RGBA - default: - // pixelType = GL_UNSIGNED_BYTE; - // pixelFormat = UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA; - pixelSize = 4; - align = 4; - break; - } - - // If using the CPU, we need 4 bytes per pixel always. - u32 bufSize = vfb->fb_stride * vfb->height * 4 * (useCPU ? 4 : pixelSize); - u32 fb_address = (0x04000000) | vfb->fb_address; - - if (vfb->fbo) { - // fbo_bind_for_read(vfb->fbo); - } else { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0"); - // fbo_unbind_read(); - return; - } - - // glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle); - if (pixelBufObj_[currentPBO_].maxSize < bufSize) { - // We reserve a buffer big enough to fit all those pixels - // glBufferData(GL_PIXEL_PACK_BUFFER, bufSize, NULL, GL_DYNAMIC_READ); - pixelBufObj_[currentPBO_].maxSize = bufSize; - } - - if (useCPU) { - // If converting pixel formats on the CPU we'll always request RGBA8888 - // SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, 0); - } else { - // Otherwise we'll directly request the format we need and let the GPU sort it out - // SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); - } - - pixelBufObj_[currentPBO_].fb_address = fb_address; - pixelBufObj_[currentPBO_].size = bufSize; - pixelBufObj_[currentPBO_].stride = vfb->fb_stride; - pixelBufObj_[currentPBO_].height = vfb->height; - pixelBufObj_[currentPBO_].format = vfb->format; - pixelBufObj_[currentPBO_].reading = true; - } - - currentPBO_ = nextPBO; -} - -void FramebufferManagerVulkan::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - -} - void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); - - vulkan2D_.BeginFrame(); - - FrameData &frame = frameData_[curFrame_]; - - 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. - VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer(); - VkRect2D scissor; - scissor.offset = { 0, 0 }; - scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ }; - vkCmdSetScissor(cmd, 0, 1, &scissor); - } else { - // Each render pass will set up scissor again. - } } void FramebufferManagerVulkan::EndFrame() { - // We flush to memory last requested framebuffer, if any. - // Only do this in the read-framebuffer modes. - if (updateVRAM_) - PackFramebufferAsync_(nullptr); - FrameData &frame = frameData_[curFrame_]; - frame.push_->End(); - - vulkan2D_.EndFrame(); - - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) { - curFrame_ = 0; - } } void FramebufferManagerVulkan::DeviceLost() { - vulkan2D_.DeviceLost(); + vulkan2D_->DeviceLost(); DestroyAllFBOs(); DestroyDeviceObjects(); @@ -963,29 +596,10 @@ void FramebufferManagerVulkan::DeviceLost() { void FramebufferManagerVulkan::DeviceRestore(VulkanContext *vulkan) { vulkan_ = vulkan; - vulkan2D_.DeviceRestore(vulkan_); + vulkan2D_->DeviceRestore(vulkan_); InitDeviceObjects(); } -std::vector FramebufferManagerVulkan::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerVulkan::DestroyAllFBOs() { currentRenderVfb_ = 0; displayFramebuf_ = 0; @@ -1006,19 +620,6 @@ void FramebufferManagerVulkan::DestroyAllFBOs() { bvfbs_.clear(); } -void FramebufferManagerVulkan::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // 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); - if (!draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS)) - Crash(); - drawEngine_->Flush(); -} - void FramebufferManagerVulkan::Resized() { FramebufferManagerCommon::Resized(); @@ -1026,92 +627,3 @@ void FramebufferManagerVulkan::Resized() { DestroyAllFBOs(); } } - -bool FramebufferManagerVulkan::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxStride) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? -/* - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GE_FORMAT_8888, false, true); - if (vfb->fbo) - fbo_bind_for_read(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - /* - fbo_unbind_read(); - - int pw = PSP_CoreParameter().pixelWidth; - int ph = PSP_CoreParameter().pixelHeight; - - // The backbuffer is flipped. - buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - /* - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, false); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - // TODO: Actually get the stencil. - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); - return true; - } - - /* - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, false); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); - - return true; - */ - return false; -} diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 8be21bdf76..9c37034842 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -22,6 +22,7 @@ #include "GPU/GPUInterface.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Vulkan/VulkanUtil.h" +#include "GPU/Vulkan/DepalettizeShaderVulkan.h" // TODO: Remove? enum VulkanFBOColorDepth { @@ -44,19 +45,7 @@ R"( vec2 texelDelta; vec4 time; )"; -// Simple struct for asynchronous PBO readbacks -// TODO: Probably will need a complete redesign. -struct AsyncPBOVulkan { - // handle; - u32 maxSize; - - u32 fb_address; - u32 stride; - u32 height; - u32 size; - GEBufferFormat format; - bool reading; -}; +class VulkanPushBuffer; class FramebufferManagerVulkan : public FramebufferManagerCommon { public: @@ -65,9 +54,9 @@ public: void SetTextureCache(TextureCacheVulkan *tc); void SetShaderManager(ShaderManagerVulkan *sm); - void SetDrawEngine(DrawEngineVulkan *td) { - drawEngine_ = td; - } + void SetDrawEngine(DrawEngineVulkan *td); + void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; } + void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } // x,y,w,h are relative to destW, destH which fill out the target completely. void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; @@ -87,22 +76,8 @@ public: void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override; - // For use when texturing from a framebuffer. May create a duplicate if target. - VulkanTexture *GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags); - - // Reads a rectangular subregion of a framebuffer to the right position in its backing memory. - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - - std::vector GetFramebufferList(); - bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; - bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; VkImageView BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); @@ -115,7 +90,6 @@ protected: void BindPostShader(const PostShaderUniforms &uniforms) override; void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override {} - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -123,16 +97,11 @@ protected: void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: - // The returned texture does not need to be free'd, might be returned from a pool (currently single entry) void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void DoNotifyDraw(); void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight); - void PackFramebufferAsync_(VirtualFramebuffer *vfb); - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); - void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -141,31 +110,21 @@ private: // Used to keep track of command buffers here but have moved all that into Thin3D. // Used by DrawPixels - VulkanTexture *drawPixelsTex_; - GEBufferFormat drawPixelsTexFormat_; - - u8 *convBuf_; - u32 convBufSize_; + VulkanTexture *drawPixelsTex_ = nullptr; + GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID; + u8 *convBuf_ = nullptr; + u32 convBufSize_ = 0; TextureCacheVulkan *textureCacheVulkan_; ShaderManagerVulkan *shaderManagerVulkan_; - DrawEngineVulkan *drawEngine_; - - AsyncPBOVulkan *pixelBufObj_; - int currentPBO_; + DrawEngineVulkan *drawEngineVulkan_; + VulkanPushBuffer *push_; + DepalShaderCacheVulkan depalVulkan_; enum { MAX_COMMAND_BUFFERS = 32, }; - // Commandbuffers are handled internally in thin3d, one for each framebuffer pass. - struct FrameData { - VulkanPushBuffer *push_; - }; - - FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - int curFrame_; - // This gets copied to the current frame's push buffer as needed. PostShaderUniforms postUniforms_; @@ -175,6 +134,9 @@ private: VkShaderModule fsBasicTex_; VkShaderModule vsBasicTex_; + VkShaderModule stencilVs_ = VK_NULL_HANDLE; + VkShaderModule stencilFs_ = VK_NULL_HANDLE; + VkPipeline cur2DPipeline_ = VK_NULL_HANDLE; // Postprocessing @@ -187,5 +149,5 @@ private: VkImageView overrideImageView_ = VK_NULL_HANDLE; // Simple 2D drawing engine. - Vulkan2D vulkan2D_; + Vulkan2D *vulkan2D_; }; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 2e6a8b151d..5fdf6d7add 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -76,7 +76,9 @@ static const VulkanCommandTableEntry commandTable[] = { GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), vulkan_((VulkanContext *)gfxCtx->GetAPIContext()), - drawEngine_(vulkan_, draw) { + drawEngine_(vulkan_, draw), + depalShaderCache_(draw, vulkan_), + vulkan2D_(vulkan_) { UpdateVsyncInterval(true); CheckGPUFeatures(); @@ -97,10 +99,14 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) framebufferManagerVulkan_->SetTextureCache(textureCacheVulkan_); framebufferManagerVulkan_->SetDrawEngine(&drawEngine_); framebufferManagerVulkan_->SetShaderManager(shaderManagerVulkan_); - textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_); + framebufferManagerVulkan_->SetVulkan2D(&vulkan2D_); textureCacheVulkan_->SetDepalShaderCache(&depalShaderCache_); + textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_); textureCacheVulkan_->SetShaderManager(shaderManagerVulkan_); textureCacheVulkan_->SetDrawEngine(&drawEngine_); + textureCacheVulkan_->SetVulkan2D(&vulkan2D_); + + InitDeviceObjects(); // Sanity check gstate if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { @@ -155,7 +161,9 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) } GPU_Vulkan::~GPU_Vulkan() { + DestroyDeviceObjects(); framebufferManagerVulkan_->DestroyAllFBOs(); + vulkan2D_.Shutdown(); depalShaderCache_.Clear(); delete textureCacheVulkan_; delete pipelineManager_; @@ -209,9 +217,19 @@ void GPU_Vulkan::BeginHostFrame() { resized_ = false; textureCacheVulkan_->StartFrame(); - depalShaderCache_.Decimate(); + + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; + + frame.push_->Reset(); + frame.push_->Begin(vulkan_); framebufferManagerVulkan_->BeginFrameVulkan(); + framebufferManagerVulkan_->SetPushBuffer(frameData_[curFrame].push_); + depalShaderCache_.SetPushBuffer(frameData_[curFrame].push_); + textureCacheVulkan_->SetPushBuffer(frameData_[curFrame].push_); + + vulkan2D_.BeginFrame(); shaderManagerVulkan_->DirtyShader(); gstate_c.Dirty(DIRTY_ALL); @@ -226,6 +244,12 @@ void GPU_Vulkan::BeginHostFrame() { } void GPU_Vulkan::EndHostFrame() { + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; + frame.push_->End(); + + vulkan2D_.EndFrame(); + drawEngine_.EndFrame(); framebufferManagerVulkan_->EndFrame(); textureCacheVulkan_->EndFrame(); @@ -600,7 +624,6 @@ void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { int bz_vcount = (op >> 8) & 0xFF; bool computeNormals = gstate.isLightingEnabled(); bool patchFacing = gstate.patchfacing & 1; - int bytesRead = 0; if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); @@ -611,6 +634,7 @@ void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { } } + int bytesRead = 0; UpdateUVScaleOffset(); drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); @@ -769,7 +793,26 @@ void GPU_Vulkan::FastLoadBoneMatrix(u32 target) { gstate.FastLoadBoneMatrix(target); } +void GPU_Vulkan::InitDeviceObjects() { + // Initialize framedata + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); + } +} + +void GPU_Vulkan::DestroyDeviceObjects() { + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { + if (frameData_[i].push_) { + frameData_[i].push_->Destroy(vulkan_); + delete frameData_[i].push_; + frameData_[i].push_ = nullptr; + } + } +} + void GPU_Vulkan::DeviceLost() { + DestroyDeviceObjects(); + framebufferManagerVulkan_->DeviceLost(); drawEngine_.DeviceLost(); pipelineManager_->DeviceLost(); @@ -839,10 +882,6 @@ void GPU_Vulkan::ClearShaderCache() { // TODO } -std::vector GPU_Vulkan::GetFramebufferList() { - return framebufferManagerVulkan_->GetFramebufferList(); -} - void GPU_Vulkan::DoState(PointerWrap &p) { GPUCommon::DoState(p); @@ -860,18 +899,6 @@ void GPU_Vulkan::DoState(PointerWrap &p) { } } -bool GPU_Vulkan::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - -bool GPU_Vulkan::DescribeCodePtr(const u8 *ptr, std::string &name) { - if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { - name = "VertexDecoderJit"; - return true; - } - return false; -} - std::vector GPU_Vulkan::DebugGetShaderIDs(DebugShaderType type) { if (type == SHADER_TYPE_VERTEXLOADER) { return drawEngine_.DebugGetVertexLoaderIDs(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index dfb3d5e81a..4c1d49a470 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -84,9 +84,6 @@ public: // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override; - std::vector GetFramebufferList() override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; protected: void FastRunLoop(DisplayList &list) override; @@ -104,6 +101,10 @@ private: void ReinitializeInternal() override; inline void UpdateVsyncInterval(bool force); void UpdateCmdInfo(); + + void InitDeviceObjects(); + void DestroyDeviceObjects(); + static CommandInfo cmdInfo_[256]; VulkanContext *vulkan_; @@ -124,4 +125,13 @@ private: std::string reportingPrimaryInfo_; std::string reportingFullInfo_; + + // Simple 2D drawing engine. + Vulkan2D vulkan2D_; + + struct FrameData { + VulkanPushBuffer *push_; + }; + + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; }; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index d23906750c..f0822a2d45 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -122,6 +122,8 @@ static bool UsesBlendConstant(int factor) { static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { + bool useBlendConstant = false; + VkPipelineColorBlendAttachmentState blend0 = {}; blend0.blendEnable = key.blendEnable; if (key.blendEnable) { @@ -166,6 +168,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip if (key.blendEnable && (UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) { dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS; + useBlendConstant = true; } dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT; @@ -285,6 +288,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip VulkanPipeline *vulkanPipeline = new VulkanPipeline(); vulkanPipeline->pipeline = pipeline; vulkanPipeline->uniformBlocks = UB_VS_FS_BASE; + vulkanPipeline->useBlendConstant = useBlendConstant; if (useHwTransform) { if (vs->HasLights()) { vulkanPipeline->uniformBlocks |= UB_VS_LIGHTS; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 7ee6770869..15e29864f1 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -64,6 +64,7 @@ enum { struct VulkanPipeline { VkPipeline pipeline; int uniformBlocks; // UB_ enum above. + bool useBlendConstant; }; class VulkanContext; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 22e9e91f58..c1d955fc99 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" +#include "thin3d/VulkanRenderManager.h" #include "math/dataconv.h" #include "GPU/Math3D.h" @@ -25,14 +26,12 @@ #include "Core/System.h" #include "Core/Config.h" #include "Core/Reporting.h" -//#include "GPU/Vulkan/StateMappingVulkan.h" #include "GPU/Vulkan/GPU_Vulkan.h" #include "GPU/Vulkan/PipelineManagerVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" -//#include "GPU/Vulkan/PixelShaderGeneratorVulkan.h" // These tables all fit into u8s. static const VkBlendFactor vkBlendFactorLookup[(size_t)BlendFactor::COUNT] = { @@ -122,8 +121,8 @@ static const VkLogicOp logicOps[] = { VK_LOGIC_OP_SET, }; -void ResetShaderBlending() { - // +void DrawEngineVulkan::ResetShaderBlending() { + boundSecondary_ = VK_NULL_HANDLE; } // TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed. @@ -135,8 +134,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - // Unfortunately, this isn't implemented yet. - gstate_c.SetAllowShaderBlend(false); + gstate_c.SetAllowShaderBlend(!g_Config.bDisableSlowFramebufEffects); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; @@ -369,42 +367,31 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } - -void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef) { +void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { // 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. + boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW); fboTexBound_ = true; fboTexNeedBind_ = false; } - */ } if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { - vkCmdSetScissor(cmd, 0, 1, &dynState_.scissor); - vkCmdSetViewport(cmd, 0, 1, &dynState_.viewport); + renderManager->SetScissor(dynState_.scissor); + renderManager->SetViewport(dynState_.viewport); } - if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { - if (dynState_.useStencil) { - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilWriteMask); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilCompareMask); - if (!applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilRef); - } - } + if ((gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE) && dynState_.useStencil) || applyStencilRef) { + renderManager->SetStencilParams(dynState_.stencilWriteMask, dynState_.stencilCompareMask, applyStencilRef ? stencilRef : dynState_.stencilRef); } - if (applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, stencilRef); - } - if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { + if (gstate_c.IsDirty(DIRTY_BLEND_STATE) && useBlendConstant) { float bc[4]; Uint8x4ToFloat4(bc, dynState_.blendColor); - vkCmdSetBlendConstants(cmd, bc); + renderManager->SetBlendFactor(bc); } -} \ No newline at end of file +} diff --git a/GPU/Vulkan/StateMappingVulkan.h b/GPU/Vulkan/StateMappingVulkan.h index e4b67a3b57..f1d8013f69 100644 --- a/GPU/Vulkan/StateMappingVulkan.h +++ b/GPU/Vulkan/StateMappingVulkan.h @@ -57,7 +57,4 @@ struct VulkanPipelineRasterStateKey { size_t size = sizeof(VulkanPipelineRasterStateKey); return memcmp(this, &other, size) < 0; } -}; - -class ShaderManagerVulkan; -void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); +}; \ No newline at end of file diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp new file mode 100644 index 0000000000..2958ba6518 --- /dev/null +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/logging.h" + +#include "ext/native/thin3d/thin3d.h" +#include "ext/native/thin3d/VulkanRenderManager.h" +#include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" +#include "GPU/Vulkan/FramebufferVulkan.h" +#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h" +#include "GPU/Vulkan/ShaderManagerVulkan.h" +#include "GPU/Vulkan/TextureCacheVulkan.h" +#include "GPU/Vulkan/VulkanUtil.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +struct StencilValueUB { + uint32_t u_stencilValue[4]; +}; + +static const char *stencil_fs = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (binding = 0) uniform sampler2D tex; +layout(push_constant) uniform params { + int u_stencilValue; +}; +layout (location = 0) in vec2 v_texcoord0; +layout (location = 0) out vec4 fragColor0; + +void main() { + vec4 index = texture(tex, v_texcoord0); + int indexBits = int(floor(index.a * 255.99)) & 0xFF; + if ((indexBits & u_stencilValue) == 0) + discard; + fragColor0 = index.aaaa; +} +)"; + +static const char stencil_vs[] = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (location = 0) out vec2 v_texcoord0; +out gl_PerVertex { vec4 gl_Position; }; +void main() { + int id = gl_VertexIndex; + v_texcoord0.x = (id == 2) ? 2.0 : 0.0; + v_texcoord0.y = (id == 1) ? 2.0 : 0.0; + gl_Position = vec4(v_texcoord0 * vec2(2.0, 2.0) + vec2(-1.0, -1.0), 0.0, 1.0); +} +)"; + +// In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without +// messing about with bitplane textures and the like. Or actually, maybe not... Let's start with +// the traditional approach. +bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { + if (!MayIntersectFramebuffer(addr)) { + return false; + } + + VirtualFramebuffer *dstBuffer = 0; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (MaskedEqual(vfb->fb_address, addr)) { + dstBuffer = vfb; + } + } + if (!dstBuffer) { + return false; + } + + int values = 0; + u8 usedBits = 0; + + const u8 *src = Memory::GetPointer(addr); + if (!src) { + return false; + } + + switch (dstBuffer->format) { + case GE_FORMAT_565: + // Well, this doesn't make much sense. + return false; + case GE_FORMAT_5551: + usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 2; + break; + case GE_FORMAT_4444: + usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 16; + break; + case GE_FORMAT_8888: + usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 256; + break; + case GE_FORMAT_INVALID: + // Impossible. + break; + } + + std::string error; + if (!stencilVs_) { + stencilVs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, stencil_vs, &error); + stencilFs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, stencil_fs, &error); + } + VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); + + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + if (usedBits == 0) { + if (skipZero) { + // Common when creating buffers, it's already 0. We're done. + return false; + } + + // TODO: Find a nice way to clear alpha here too. + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + return true; + } + + shaderManagerVulkan_->DirtyLastShader(); + textureCacheVulkan_->ForgetLastTexture(); + + u16 w = dstBuffer->renderWidth; + u16 h = dstBuffer->renderHeight; + float u1 = 1.0f; + 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::RPAction::KEEP, Draw::RPAction::CLEAR }); + } else { + // something is wrong... + } + + VkPipeline pipeline = vulkan2D_->GetPipeline(rp, stencilVs_, stencilFs_, false, Vulkan2D::VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS); + renderManager->BindPipeline(pipeline); + renderManager->SetViewport({ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }); + renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } }); + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE); + + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); + + for (int i = 1; i < values; i += i) { + if (!(usedBits & i)) { + // It's already zero, let's skip it. + continue; + } + // These feel a little backwards : Mask is the bits that are going to be written, while value + // is the "mask" that will be tested against. + uint8_t mask = 0; + uint32_t value = 0; + if (dstBuffer->format == GE_FORMAT_4444) { + mask = i | (i << 4); + value = i * 16; + } else if (dstBuffer->format == GE_FORMAT_5551) { + mask = 0xFF; + value = i * 128; + } else { + mask = i; + value = i; + } + renderManager->SetStencilParams(mask, 0xFF, 0xFF); + renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle + } + + overrideImageView_ = VK_NULL_HANDLE; + RebindFramebuffer(); + return true; +} diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 47dd0d57a9..de2167271f 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -23,6 +23,8 @@ #include "math/math_util.h" #include "profiler/profiler.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" + #include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/Host.h" @@ -130,10 +132,19 @@ TextureCacheVulkan::TextureCacheVulkan(Draw::DrawContext *draw, VulkanContext *v lastBoundTexture = nullptr; allocator_ = new VulkanDeviceAllocator(vulkan_, TEXCACHE_MIN_SLAB_SIZE, TEXCACHE_MAX_SLAB_SIZE); + VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.magFilter = VK_FILTER_NEAREST; + samp.minFilter = VK_FILTER_NEAREST; + samp.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &samplerNearest_); SetupTextureDecoder(); } TextureCacheVulkan::~TextureCacheVulkan() { + vulkan_->Delete().QueueDeleteSampler(samplerNearest_); Clear(true); if (allocator_) { @@ -152,6 +163,11 @@ void TextureCacheVulkan::SetFramebufferManager(FramebufferManagerVulkan *fbManag framebufferManager_ = fbManager; } +void TextureCacheVulkan::SetVulkan2D(Vulkan2D *vk2d) { + vulkan2D_ = vk2d; + depalShaderCache_->SetVulkan2D(vk2d); +} + void TextureCacheVulkan::DeviceLost() { Clear(true); @@ -274,6 +290,8 @@ void TextureCacheVulkan::SetFramebufferSamplingParams(u16 bufferWidth, u16 buffe void TextureCacheVulkan::StartFrame() { InvalidateLastTexture(); + depalShaderCache_->Decimate(); + timesInvalidatedAllThisFrame_ = 0; texelsScaledThisFrame_ = 0; @@ -331,61 +349,42 @@ void TextureCacheVulkan::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutB void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) { if (!entry || !entry->vkTex) { imageView_ = VK_NULL_HANDLE; - sampler_ = VK_NULL_HANDLE; + curSampler_ = VK_NULL_HANDLE; return; } imageView_ = entry->vkTex->texture_->GetImageView(); SamplerCacheKey key; UpdateSamplingParams(*entry, key); - sampler_ = samplerCache_.GetOrCreateSampler(key); + curSampler_ = samplerCache_.GetOrCreateSampler(key); } void TextureCacheVulkan::Unbind() { imageView_ = VK_NULL_HANDLE; - sampler_ = VK_NULL_HANDLE; + curSampler_ = VK_NULL_HANDLE; InvalidateLastTexture(); } void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { - DepalShaderVulkan *depal = nullptr; + DepalShaderVulkan *depalShader = nullptr; const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - // depal = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); + depalShader = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); } - if (depal) { - // VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - // VulkanFBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888); + if (depalShader) { + depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData()); + VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - //depalFBO->BeginPass(cmd); + Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO( + framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); - struct Pos { - Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { - } - float x; - float y; - float z; + Vulkan2D::Vertex verts[4] = { + { -1, -1, 0.0f, 0, 0 }, + { 1, -1, 0.0f, 1, 0 }, + { -1, 1, 0.0f, 0, 1 }, + { 1, 1, 0.0f, 1, 1 }, }; - struct UV { - UV(float u_, float v_) : u(u_), v(v_) { - } - float u; - float v; - }; - - Pos pos[4] = { - { -1, -1, -1 }, - { 1, -1, -1 }, - { 1, 1, -1 }, - { -1, 1, -1 }, - }; - UV uv[4] = { - { 0, 0 }, - { 1, 0 }, - { 1, 1 }, - { 0, 1 }, - }; - static const int indices[4] = { 0, 1, 3, 2 }; // If min is not < max, then we don't have values (wasn't set during decode.) if (gstate_c.vertBounds.minV < gstate_c.vertBounds.maxV) { @@ -405,27 +404,43 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr const float top = v1 * invHalfHeight - 1.0f; const float bottom = v2 * invHalfHeight - 1.0f; // Points are: BL, BR, TR, TL. - pos[0] = Pos(left, bottom, -1.0f); - pos[1] = Pos(right, bottom, -1.0f); - pos[2] = Pos(right, top, -1.0f); - pos[3] = Pos(left, top, -1.0f); + verts[0].x = left; + verts[0].y = bottom; + verts[1].x = right; + verts[1].y = bottom; + verts[2].x = left; + verts[2].y = top; + verts[3].x = right; + verts[3].y = top; // And also the UVs, same order. const float uvleft = u1 * invWidth; const float uvright = u2 * invWidth; const float uvtop = v1 * invHeight; const float uvbottom = v2 * invHeight; - uv[0] = UV(uvleft, uvbottom); - uv[1] = UV(uvright, uvbottom); - uv[2] = UV(uvright, uvtop); - uv[3] = UV(uvleft, uvtop); + verts[0].u = uvleft; + verts[0].v = uvbottom; + verts[1].u = uvright; + verts[1].v = uvbottom; + verts[2].u = uvleft; + verts[2].v = uvtop; + verts[3].u = uvright; + verts[3].v = uvtop; } - shaderManagerVulkan_->DirtyLastShader(); + VkBuffer pushed; + uint32_t offset = push_->PushAligned(verts, sizeof(verts), 4, &pushed); - //depalFBO->EndPass(cmd); - //depalFBO->TransitionToTexture(cmd); - //imageView = depalFBO->GetColorImageView(); + draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, Draw::FB_COLOR_BIT, 0); + VkImageView fbo = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); + + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->BindPipeline(depalShader->pipeline); + renderManager->SetScissor(VkRect2D{ {0, 0}, { framebuffer->renderWidth, framebuffer->renderHeight} }); + renderManager->SetViewport(VkViewport{ 0.f, 0.f, (float)framebuffer->renderWidth, (float)framebuffer->renderHeight, 0.f, 1.f }); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, pushed, offset, 4); + shaderManagerVulkan_->DirtyLastShader(); const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; @@ -434,10 +449,16 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE); - // imageView_ = depalFbo->getImageView(). + framebufferManager_->RebindFramebuffer(); + draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); + imageView_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); + + // Need to rebind the pipeline since we switched it. + drawEngine_->DirtyPipeline(); } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; + framebufferManager_->RebindFramebuffer(); imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); @@ -446,7 +467,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr SamplerCacheKey samplerKey; SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey); - sampler_ = samplerCache_.GetOrCreateSampler(samplerKey); + curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); InvalidateLastTexture(entry); } @@ -471,6 +492,7 @@ VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) { void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceImages) { entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // For the estimate, we assume cluts always point to 8888 for simplicity. cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); @@ -533,6 +555,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm int h = gstate.getTextureHeight(0); ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { + if (replaceImages) { + // Since we're replacing the texture, we can't replace the image inside. + ReleaseTexture(entry, true); + replaceImages = false; + } // We're replacing, so we won't scale. scaleFactor = 1; entry->status |= TexCacheEntry::STATUS_IS_SCALED; @@ -569,7 +596,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm if (replaced.Valid()) { actualFmt = ToVulkanFormat(replaced.Format(0)); } - if (!entry->vkTex) { + + { + delete entry->vkTex; entry->vkTex = new CachedTextureVulkan(); entry->vkTex->texture_ = new VulkanTexture(vulkan_, allocator_); VulkanTexture *image = entry->vkTex->texture_; @@ -593,7 +622,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm break; } - bool allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -612,15 +641,13 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { delete entry->vkTex; entry->vkTex = nullptr; } - } else { - entry->vkTex->texture_->TransitionForUpload(); } lastBoundTexture = entry->vkTex; @@ -657,7 +684,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } else { if (fakeMipmap) { LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt); - entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, 0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); break; } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); @@ -666,7 +693,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight); } } - entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); } if (maxLevel == 0) { @@ -679,7 +706,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } } - entry->vkTex->texture_->EndCreate(); + entry->vkTex->texture_->EndCreate(cmdInit); gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN); @@ -783,3 +810,8 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt } } } + +bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { + // TODO + return false; +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 809606aa00..1b168fd0f5 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -61,6 +61,7 @@ private: DenseHashMap cache_; }; +class Vulkan2D; class TextureCacheVulkan : public TextureCacheCommon { public: @@ -83,6 +84,10 @@ public: void SetDrawEngine(DrawEngineVulkan *td) { drawEngine_ = td; } + void SetVulkan2D(Vulkan2D *vk2d); + void SetPushBuffer(VulkanPushBuffer *push) { + push_ = push; + } void ForgetLastTexture() override { lastBoundTexture = nullptr; @@ -96,10 +101,12 @@ public: void GetVulkanHandles(VkImageView &imageView, VkSampler &sampler) { imageView = imageView_; - sampler = sampler_; + sampler = curSampler_; } void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; + protected: void BindTexture(TexCacheEntry *entry) override; void Unbind() override; @@ -117,6 +124,7 @@ private: VulkanContext *vulkan_; VulkanDeviceAllocator *allocator_; + VulkanPushBuffer *push_; SamplerCache samplerCache_; @@ -132,10 +140,13 @@ private: DepalShaderCacheVulkan *depalShaderCache_; ShaderManagerVulkan *shaderManagerVulkan_; DrawEngineVulkan *drawEngine_; + Vulkan2D *vulkan2D_; // Bound state to emulate an API similar to the others VkImageView imageView_ = VK_NULL_HANDLE; - VkSampler sampler_ = VK_NULL_HANDLE; + VkSampler curSampler_ = VK_NULL_HANDLE; + + VkSampler samplerNearest_ = VK_NULL_HANDLE; }; VkFormat getClutDestFormatVulkan(GEPaletteFormat format); diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index efda658f88..45c3b8e994 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -636,7 +636,7 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses if (doBezier || doSpline) WRITE(p, " v_texcoord = vec3(tex.xy * base.uvscaleoffset.xy + base.uvscaleoffset.zw, 0.0);\n"); else - WRITE(p, " v_texcoord = vec3(texcoord.xy, 0.0);\n"); + WRITE(p, " v_texcoord = vec3(texcoord.xy * base.uvscaleoffset.xy, 0.0);\n"); } else { WRITE(p, " v_texcoord = vec3(0.0);\n"); } diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index b9d8e905e9..14ba80dccb 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -20,34 +20,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "GPU/Vulkan/VulkanUtil.h" -VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {} - -VulkanFBO::~VulkanFBO() { - delete color_; - delete depthStencil_; -} - -void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { - color_ = new VulkanTexture(vulkan); - VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); - depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); - - VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; - - VkFramebufferCreateInfo fb = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb.pAttachments = views; - fb.attachmentCount = 2; - fb.flags = 0; - fb.renderPass = rp_compatible; - fb.width = width; - fb.height = height; - fb.layers = 1; - - vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_); -} - -Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan), curFrame_(0) { +Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan) { InitDeviceObjects(); } @@ -63,6 +36,7 @@ void Vulkan2D::DestroyDeviceObjects() { for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { if (frameData_[i].descPool != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteDescriptorPool(frameData_[i].descPool); + frameData_[i].descPool = VK_NULL_HANDLE; } } for (auto it : pipelines_) { @@ -79,9 +53,16 @@ void Vulkan2D::DestroyDeviceObjects() { vkDestroyPipelineLayout(device, pipelineLayout_, nullptr); pipelineLayout_ = VK_NULL_HANDLE; } + + // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. + if (pipelineCache_ != VK_NULL_HANDLE) { + vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); + pipelineCache_ = VK_NULL_HANDLE; + } } void Vulkan2D::InitDeviceObjects() { + pipelineCache_ = vulkan_->CreatePipelineCache(); // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. VkDescriptorSetLayoutBinding bindings[2] = {}; bindings[0].descriptorCount = 1; @@ -102,12 +83,12 @@ void Vulkan2D::InitDeviceObjects() { assert(VK_SUCCESS == res); VkDescriptorPoolSize dpTypes[1]; - dpTypes[0].descriptorCount = 1500; + dpTypes[0].descriptorCount = 3000; dpTypes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later. - dp.maxSets = 1500; + dp.maxSets = 3000; dp.pPoolSizes = dpTypes; dp.poolSizeCount = ARRAY_SIZE(dpTypes); for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { @@ -117,7 +98,7 @@ void Vulkan2D::InitDeviceObjects() { VkPushConstantRange push = {}; push.offset = 0; - push.size = 32; + push.size = 16; push.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; @@ -140,15 +121,13 @@ void Vulkan2D::DeviceRestore(VulkanContext *vulkan) { } void Vulkan2D::BeginFrame() { - FrameData &frame = frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; frame.descSets.clear(); vkResetDescriptorPool(vulkan_->GetDevice(), frame.descPool, 0); } void Vulkan2D::EndFrame() { - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) - curFrame_ = 0; } VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2) { @@ -158,7 +137,8 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, key.sampler[0] = sampler1; key.sampler[1] = sampler2; - FrameData *frame = &frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData *frame = &frameData_[curFrame]; auto iter = frame->descSets.find(key); if (iter != frame->descSets.end()) { return iter->second; @@ -215,11 +195,13 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, return desc; } -VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs) { +VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices, VK2DDepthStencilMode depthStencilMode) { PipelineKey key; key.vs = vs; key.fs = fs; key.rp = rp; + key.depthStencilMode = depthStencilMode; + key.readVertices = readVertices; auto iter = pipelines_.find(key); if (iter != pipelines_.end()) { @@ -228,7 +210,7 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade VkPipelineColorBlendAttachmentState blend0 = {}; blend0.blendEnable = false; - blend0.colorWriteMask = 0xF; + blend0.colorWriteMask = depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS ? 0 : 0xF; VkPipelineColorBlendStateCreateInfo cbs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cbs.pAttachments = &blend0; @@ -237,13 +219,32 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade VkPipelineDepthStencilStateCreateInfo dss = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; dss.depthBoundsTestEnable = false; - dss.stencilTestEnable = false; dss.depthTestEnable = false; + dss.stencilTestEnable = false; + switch (depthStencilMode) { + case VK2DDepthStencilMode::NONE: + break; + case VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS: + dss.stencilTestEnable = true; + dss.front.reference = 0xFF; + dss.front.compareMask = 0xFF; + dss.front.compareOp = VK_COMPARE_OP_ALWAYS; + dss.front.depthFailOp = VK_STENCIL_OP_REPLACE; + dss.front.failOp = VK_STENCIL_OP_REPLACE; + dss.front.passOp = VK_STENCIL_OP_REPLACE; + dss.back = dss.front; + break; + } - VkDynamicState dynamicStates[2]; + VkDynamicState dynamicStates[5]; int numDyn = 0; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT; + if (depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS) { + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; + } VkPipelineDynamicStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; ds.pDynamicStates = dynamicStates; @@ -290,10 +291,10 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade ibd.stride = vertexStride; VkPipelineVertexInputStateCreateInfo vis = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vis.vertexBindingDescriptionCount = 1; - vis.pVertexBindingDescriptions = &ibd; - vis.vertexAttributeDescriptionCount = attributeCount; - vis.pVertexAttributeDescriptions = attrs; + vis.vertexBindingDescriptionCount = readVertices ? 1 : 0; + vis.pVertexBindingDescriptions = readVertices ? &ibd : nullptr; + vis.vertexAttributeDescriptionCount = readVertices ? attributeCount : 0; + vis.pVertexAttributeDescriptions = readVertices ? attrs : nullptr; VkPipelineViewportStateCreateInfo views = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; views.viewportCount = 1; @@ -311,7 +312,6 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade pipe.pDepthStencilState = &dss; pipe.pRasterizationState = &rs; - // We will use dynamic viewport state. pipe.pVertexInputState = &vis; pipe.pViewportState = &views; pipe.pTessellationState = nullptr; @@ -325,7 +325,7 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade pipe.subpass = 0; VkPipeline pipeline; - VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), cache, 1, &pipe, nullptr, &pipeline); + VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pipe, nullptr, &pipeline); if (result == VK_SUCCESS) { pipelines_[key] = pipeline; return pipeline; @@ -334,11 +334,6 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade } } -void Vulkan2D::BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1) { - VkDescriptorSet descSet = GetDescriptorSet(tex1, sampler1, VK_NULL_HANDLE, VK_NULL_HANDLE); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 0, nullptr); -} - VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) { std::vector spirv; bool success = GLSLtoSPV(stage, code, spirv, error); diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 630bebd05f..6a6f526236 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -42,38 +42,13 @@ // // Each FBO will get its own command buffer for each pass. -// -struct VulkanFBOPass { - VkCommandBuffer cmd; -}; - -class VulkanFBO { -public: - VulkanFBO(); - ~VulkanFBO(); - - // Depth-format is chosen automatically depending on hardware support. - // Color format will be 32-bit RGBA. - void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); - - VulkanTexture *GetColor() { return color_; } - VulkanTexture *GetDepthStencil() { return depthStencil_; } - - VkFramebuffer GetFramebuffer() { return framebuffer_; } - -private: - VulkanTexture *color_; - VulkanTexture *depthStencil_; - - // This point specifically to color and depth. - VkFramebuffer framebuffer_; -}; - // Similar to a subset of Thin3D, but separate. // This is used for things like postprocessing shaders, depal, etc. // No UBO data is used, only PushConstants. // No transform matrices, only post-proj coordinates. // Two textures can be sampled. +// Some simplified depth/stencil modes available. + class Vulkan2D { public: Vulkan2D(VulkanContext *vulkan); @@ -83,16 +58,20 @@ public: void DeviceRestore(VulkanContext *vulkan); void Shutdown(); - VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); + enum class VK2DDepthStencilMode { + NONE, + STENCIL_REPLACE_ALWAYS, // Does not draw to color. + }; + // The only supported primitive is the triangle strip, for simplicity. + // ReadVertices can be used for vertex-less rendering where you generate verts in the vshader. + VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices = true, VK2DDepthStencilMode depthStencilMode = VK2DDepthStencilMode::NONE); + VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); void EndFrame(); VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2); - // Simple way - void BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1); - struct Vertex { float x, y, z; float u, v; @@ -102,9 +81,10 @@ private: void InitDeviceObjects(); void DestroyDeviceObjects(); - VulkanContext *vulkan_; - VkDescriptorSetLayout descriptorSetLayout_; - VkPipelineLayout pipelineLayout_; + VulkanContext *vulkan_ = nullptr; + VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; // Yes, another one... struct DescriptorSetKey { @@ -121,8 +101,10 @@ private: VkShaderModule vs; VkShaderModule fs; VkRenderPass rp; + VK2DDepthStencilMode depthStencilMode; + bool readVertices; bool operator < (const PipelineKey &other) const { - return std::tie(vs, fs, rp) < std::tie(other.vs, other.fs, other.rp); + return std::tie(vs, fs, rp, depthStencilMode, readVertices) < std::tie(other.vs, other.fs, other.rp, depthStencilMode, readVertices); } }; @@ -132,7 +114,6 @@ private: }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - int curFrame_ = 0; std::map pipelines_; }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 1a43d7c720..30a5eb7735 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1007,6 +1007,8 @@ void EmuScreen::postRender() { Draw::DrawContext *draw = screenManager()->getDrawContext(); if (!draw) return; + if (invalid_) + draw->WipeQueue(); draw->EndFrame(); } diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index f79045110f..828b6b150d 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 9bb7322606..b4e4bb6ea9 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 @@ -405,6 +405,7 @@ + @@ -596,6 +597,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 0d7268912a..119ffa5bc1 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -617,6 +617,9 @@ MIPS\IR + + HLE + @@ -1152,5 +1155,8 @@ MIPS\IR + + HLE + \ No newline at end of file diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index b78ee46e09..b7d1f40921 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index 9c2153302f..f107dbabb7 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 @@ -1180,6 +1180,7 @@ + NotUsing @@ -1315,4 +1316,4 @@ - + \ No newline at end of file diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index b009776eec..c7e48c5f7a 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -454,6 +454,9 @@ base + + math + @@ -720,4 +723,4 @@ gfx - + \ No newline at end of file diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 7f764c03be..96d346b819 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "PPSSPP_UWPMain.h" +#include #include #include "base/basictypes.h" @@ -323,6 +324,8 @@ void PPSSPP_UWPMain::LoadStorageFile(StorageFile ^file) { UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr resources) { draw_ = Draw::T3DCreateD3D11Context( resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetDeviceFeatureLevel(), 0); + bool success = draw_->CreatePresets(); + assert(success); } void UWPGraphicsContext::Shutdown() { diff --git a/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj b/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj index 8acb031408..d36a6fec45 100644 --- a/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj +++ b/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/StorageFileLoader.cpp b/UWP/StorageFileLoader.cpp index 9e90b8b2ef..f758912460 100644 --- a/UWP/StorageFileLoader.cpp +++ b/UWP/StorageFileLoader.cpp @@ -137,18 +137,6 @@ void StorageFileLoader::EnsureOpen() { Sleep(100); } -// Note that multithreaded use could wreak havoc with this... -void StorageFileLoader::Seek(s64 absolutePos) { - EnsureOpen(); - seekPos_ = absolutePos; -} - -size_t StorageFileLoader::Read(size_t bytes, size_t count, void *data, Flags flags) { - size_t bytesRead = ReadAt(seekPos_, bytes, count, data, flags); - seekPos_ += bytesRead; - return bytesRead; -} - size_t StorageFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) { EnsureOpen(); if (operationRequested_ || responseAvailable_) diff --git a/UWP/StorageFileLoader.h b/UWP/StorageFileLoader.h index 11b35ecb0e..0a85a533bd 100644 --- a/UWP/StorageFileLoader.h +++ b/UWP/StorageFileLoader.h @@ -26,9 +26,7 @@ public: s64 FileSize() override; std::string Path() const override; std::string Extension() override; - void Seek(s64 absolutePos) override; - size_t Read(size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; private: @@ -87,4 +85,4 @@ private: class StorageDirectoryWrapper { private: std::thread thread_; -}; \ No newline at end of file +}; diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index eada47b3b6..377dff98d5 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 4be8a7258b..16f370ea04 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -46,67 +46,67 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 PPSSPP_UWP + 10.0.15063.0 Application true - v140 + v141 Application true - v140 + v141 Application true - v140 + v141 Application false true - v140 + v141 true Application false true - v140 + v141 true Application false false - v140 + v141 true Application false false - v140 + v141 true Application false true - v140 + v141 true Application false true - v140 + v141 true diff --git a/UWP/UWPHost.cpp b/UWP/UWPHost.cpp index b7281b95ad..72dd263857 100644 --- a/UWP/UWPHost.cpp +++ b/UWP/UWPHost.cpp @@ -176,10 +176,6 @@ bool UWPHost::CreateDesktopShortcut(std::string argumentPath, std::string gameTi return false; } -void UWPHost::GoFullscreen(bool viewFullscreen) { - // TODO -} - void UWPHost::ToggleDebugConsoleVisibility() { // N/A } diff --git a/UWP/UWPHost.h b/UWP/UWPHost.h index 75353b5cc3..635e37bccc 100644 --- a/UWP/UWPHost.h +++ b/UWP/UWPHost.h @@ -44,8 +44,6 @@ public: void NotifyUserMessage(const std::string &message, float duration = 1.0f, u32 color = 0x00FFFFFF, const char *id = nullptr) override; - void GoFullscreen(bool) override; - GraphicsContext *GetGraphicsContext() { return nullptr; } private: diff --git a/UWP/glslang_UWP/glslang_UWP.vcxproj b/UWP/glslang_UWP/glslang_UWP.vcxproj index 44b73b2814..5673b2a5be 100644 --- a/UWP/glslang_UWP/glslang_UWP.vcxproj +++ b/UWP/glslang_UWP/glslang_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/libkirk_UWP/libkirk_UWP.vcxproj b/UWP/libkirk_UWP/libkirk_UWP.vcxproj index 48113e0c0b..49ecaec065 100644 --- a/UWP/libkirk_UWP/libkirk_UWP.vcxproj +++ b/UWP/libkirk_UWP/libkirk_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/zlib_UWP/zlib_UWP.vcxproj b/UWP/zlib_UWP/zlib_UWP.vcxproj index 7d858a7cc5..ce7b30285c 100644 --- a/UWP/zlib_UWP/zlib_UWP.vcxproj +++ b/UWP/zlib_UWP/zlib_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 3e0a2cd5af..cb68ae915a 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -61,8 +61,7 @@ void CGEDebugger::Init() { } CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) - : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent), primaryWindow(nullptr), secondWindow(nullptr), - textureLevel_(0), showClut_(false), primaryBuffer_(nullptr), secondBuffer_(nullptr) { + : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) { GPUBreakpoints::Init(); Core_ListenShutdown(ForceUnpause); diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index 10ca029945..f4f6aa3db5 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -85,25 +85,25 @@ private: u32 TexturePreviewFlags(const GPUgstate &state); - CtrlDisplayListView *displayList; - TabDisplayLists *lists; - TabStateFlags *flags; - TabStateLighting *lighting; - TabStateTexture *textureState; - TabStateSettings *settings; - TabVertices *vertices; - TabMatrices *matrices; - SimpleGLWindow *primaryWindow; - SimpleGLWindow *secondWindow; - TabStateWatch *watch; - TabControl *tabs; - TabControl *fbTabs; - int textureLevel_; - bool showClut_; - bool forceOpaque_; + CtrlDisplayListView *displayList = nullptr; + TabDisplayLists *lists = nullptr; + TabStateFlags *flags = nullptr; + TabStateLighting *lighting = nullptr; + TabStateTexture *textureState = nullptr; + TabStateSettings *settings = nullptr; + TabVertices *vertices = nullptr; + TabMatrices *matrices = nullptr; + SimpleGLWindow *primaryWindow = nullptr; + SimpleGLWindow *secondWindow = nullptr; + TabStateWatch *watch = nullptr; + TabControl *tabs = nullptr; + TabControl *fbTabs = nullptr; + int textureLevel_ = 0; + bool showClut_ = false; + bool forceOpaque_ = false; // The most recent primary/framebuffer and texture buffers. - const GPUDebugBuffer *primaryBuffer_; - const GPUDebugBuffer *secondBuffer_; + const GPUDebugBuffer *primaryBuffer_ = nullptr; + const GPUDebugBuffer *secondBuffer_ = nullptr; int minWidth_; int minHeight_; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index ffd87e29ee..bf7765c771 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -2,6 +2,7 @@ #include "Common/CommonWindows.h" #include +#include #include "base/logging.h" #include "util/text/utf8.h" @@ -27,10 +28,6 @@ D3D11Context::~D3D11Context() { void D3D11Context::SwapBuffers() { swapChain_->Present(0, 0); draw_->HandleEvent(Draw::Event::PRESENTED, 0, 0, nullptr, nullptr); - - // Might be a good idea. - // context_->ClearState(); - // } void D3D11Context::SwapInterval(int interval) { @@ -143,6 +140,8 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { #endif draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_); + bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think. + assert(success); int width; int height; diff --git a/Windows/GPU/D3D9Context.cpp b/Windows/GPU/D3D9Context.cpp index 096251677f..ec7ffdb0ca 100644 --- a/Windows/GPU/D3D9Context.cpp +++ b/Windows/GPU/D3D9Context.cpp @@ -165,6 +165,13 @@ bool D3D9Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { //deviceEx->SetMaximumFrameLatency(1); } draw_ = Draw::T3DCreateDX9Context(d3d_, d3dEx_, adapterId_, device_, deviceEx_); + if (!draw_->CreatePresets()) { + // Shader compiler not installed? Return an error so we can fall back to GL. + device_->Release(); + d3d_->Release(); + *error_message = "DirectX9 runtime not correctly installed. Please install."; + return false; + } if (draw_) draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, 0, 0, nullptr); return true; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index d1b6b73563..c73c0cd298 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -15,8 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -// TODO: What a mess this is :( - +#include #include "Common/Log.h" #include "Common/CommonWindows.h" #include "gfx/gl_common.h" @@ -358,6 +357,8 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these. + assert(success); CHECK_GL_ERROR_IF_DEBUG(); return true; // Success } diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 0f4d18118f..21dcb026ea 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -124,19 +124,11 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep } message << "[" << pLayerPrefix << "] " << ObjTypeToString(objType) << " Code " << msgCode << " : " << pMsg << "\n"; - if (msgCode == 2) // Useless perf warning + if (msgCode == 2) // Useless perf warning ("Vertex attribute at location X not consumed by vertex shader") 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")) + if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. 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(); OutputDebugStringA(msg.c_str()); @@ -180,7 +172,13 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); - if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_MAILBOX)) { + + // int vulkanFlags = VULKAN_FLAG_PRESENT_FIFO_RELAXED; + int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; + if (g_validate_) { + vulkanFlags |= VULKAN_FLAG_VALIDATE; + } + if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), vulkanFlags)) { *error_message = g_Vulkan->InitError(); return false; } @@ -197,17 +195,21 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } g_Vulkan->InitSurfaceWin32(hInst, hWnd); - if (!g_Vulkan->InitObjects(true)) { + if (!g_Vulkan->InitObjects()) { Shutdown(); return false; } draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); - + bool success = draw_->CreatePresets(); + assert(success); // Doesn't fail, we include the compiler. + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); return true; } void WindowsVulkanContext::Shutdown() { + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + delete draw_; draw_ = nullptr; @@ -226,10 +228,13 @@ void WindowsVulkanContext::SwapBuffers() { void WindowsVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); g_Vulkan->ReinitSurfaceWin32(); - g_Vulkan->InitObjects(true); + + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void WindowsVulkanContext::SwapInterval(int interval) { diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 34c483eab2..8ef8b5c6a5 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -8,7 +8,7 @@ + android:targetSdkVersion="26" /> diff --git a/android/build.gradle b/android/build.gradle index 80455784fd..097a02e654 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,8 +35,8 @@ android { } } } - compileSdkVersion 25 - buildToolsVersion '25.0.0' + compileSdkVersion 26 + buildToolsVersion '27.0.0' defaultConfig { applicationId 'org.ppsspp.ppsspp' if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) { @@ -52,14 +52,13 @@ android { new File("versioncode.txt").write(androidGitVersion.code().toString()) minSdkVersion 9 - targetSdkVersion 25 + targetSdkVersion 26 if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) { versionCode ANDROID_VERSION_CODE versionName ANDROID_VERSION_NAME } ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' - // Disabled 'x86_64' due to reported crash issues I can't debug without a device. + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } signingConfig signingConfigs.debug } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 8cd5ba7603..f0f3c0ab12 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -136,6 +136,7 @@ VULKAN_FILES := \ $(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \ + $(SRC)/GPU/Vulkan/StencilBufferVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureScalerVulkan.cpp \ $(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \ @@ -222,6 +223,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \ $(SRC)/GPU/Common/TextureScalerCommon.cpp.arm \ $(SRC)/GPU/Common/ShaderCommon.cpp \ + $(SRC)/GPU/Common/StencilCommon.cpp \ $(SRC)/GPU/Common/SplineCommon.cpp.arm \ $(SRC)/GPU/Common/DrawEngineCommon.cpp.arm \ $(SRC)/GPU/Common/TransformCommon.cpp.arm \ diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index c04d46c309..e0e772d1c1 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -133,6 +133,8 @@ bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, in gl->MakeCurrent(); CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these. + assert(success); return true; } @@ -157,6 +159,8 @@ public: AndroidJavaEGLGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); + assert(success); } ~AndroidJavaEGLGraphicsContext() { delete draw_; @@ -308,17 +312,20 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } - g_Vulkan->InitObjects(true); + g_Vulkan->InitObjects(); draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); + bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. + assert(success); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); return true; } void AndroidVulkanContext::Shutdown() { ILOG("AndroidVulkanContext::Shutdown"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); delete draw_; draw_ = nullptr; NativeShutdownGraphics(); - g_Vulkan->WaitUntilQueueIdle(); g_Vulkan->DestroyObjects(); g_Vulkan->DestroyDebugMsgCallback(); @@ -335,11 +342,13 @@ void AndroidVulkanContext::SwapBuffers() { void AndroidVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); // backbufferResize updated these values. TODO: Notify another way? g_Vulkan->ReinitSurfaceAndroid(pixel_xres, pixel_yres); - g_Vulkan->InitObjects(g_Vulkan); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void AndroidVulkanContext::SwapInterval(int interval) { diff --git a/android/project.properties b/android/project.properties index 8b8e6098e7..5e19b7a0b1 100644 --- a/android/project.properties +++ b/android/project.properties @@ -9,4 +9,4 @@ # # Project target. -target=android-25 +target=android-26 diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 39adc3b0b2..b48cfae5c1 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -226,7 +226,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } break; case REQUEST_CODE_CAMERA_PERMISSION: - if (permissionsGranted(permissions, grantResults)) { + if (mCameraHelper != null && permissionsGranted(permissions, grantResults)) { mCameraHelper.startCamera(); } break; @@ -332,7 +332,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } mLocationHelper = new LocationHelper(this); - mCameraHelper = new CameraHelper(this); + if (Build.VERSION.SDK_INT >= 11) { + // android.graphics.SurfaceTexture is not available before version 11. + mCameraHelper = new CameraHelper(this); + } } @TargetApi(24) @@ -1209,10 +1212,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } else if (command.equals("camera_command")) { if (params.equals("startVideo")) { - if (!askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { + if (mCameraHelper != null && !askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { mCameraHelper.startCamera(); } - } else if (params.equals("stopVideo")) { + } else if (mCameraHelper != null && params.equals("stopVideo")) { mCameraHelper.stopCamera(); } } diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index d0be78b895..d05c7b4348 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -184,3 +184,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 050000004c0500006802000000810000,PLAYSTATION(R)3 Controller,platform:Linux,x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4, 03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5, 05000000c82d00002038000000010000,8Bitdo NES30 Pro,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b2,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, +030000005e040000a102000000010000,Xbox 360 Wireless Receiver,b:b1,righty:a3,rightx:a2,a:b0,x:b2,righttrigger:b7,rightstick:b12,leftshoulder:b4,dpup:b15,start:b9,dpdown:b16,rightshoulder:b5,dpright:b14,lefttrigger:b6,leftstick:b11,guide:b8,y:b3,lefty:a1,leftx:a0,back:b8,dpleft:b13,platform:Linux, diff --git a/assets/lang b/assets/lang index c31a1d3096..4fddbc9d30 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit c31a1d309631acbe0f2d583f390f163e18befe5d +Subproject commit 4fddbc9d3032113bdfed5186de73545e19340b4f diff --git a/build.gradle b/build.gradle index e994947a78..62b62b545c 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:3.0.0' } } diff --git a/cmake/Toolchains/raspberry.armv6.cmake b/cmake/Toolchains/raspberry.armv6.cmake index 54182a6a6a..3d93462dab 100644 --- a/cmake/Toolchains/raspberry.armv6.cmake +++ b/cmake/Toolchains/raspberry.armv6.cmake @@ -19,7 +19,8 @@ set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vc/lib" CACHE STRING "" FORCE) -set(OPENGL_LIBRARIES GLESv2 bcm_host) +set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) +set(EGL_LIBRARIES brcmEGL) set(USING_GLES2 ON) set(USING_FBDEV ON) diff --git a/cmake/Toolchains/raspberry.armv7.cmake b/cmake/Toolchains/raspberry.armv7.cmake index 870a3d4489..eaff3d479d 100644 --- a/cmake/Toolchains/raspberry.armv7.cmake +++ b/cmake/Toolchains/raspberry.armv7.cmake @@ -20,7 +20,8 @@ set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vc/lib" CACHE STRING "" FORCE) -set(OPENGL_LIBRARIES GLESv2 bcm_host) +set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) +set(EGL_LIBRARIES brcmEGL) set(USING_GLES2 ON) set(USING_FBDEV ON) set(ARMV7 ON) diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 14310e2276..ad1271daba 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -85,6 +85,8 @@ LOCAL_SRC_FILES :=\ thin3d/thin3d.cpp \ thin3d/thin3d_gl.cpp \ thin3d/thin3d_vulkan.cpp \ + thin3d/VulkanRenderManager.cpp \ + thin3d/VulkanQueueRunner.cpp \ ui/view.cpp \ ui/viewgroup.cpp \ ui/ui.cpp \ diff --git a/ext/native/base/PCMain.cpp b/ext/native/base/PCMain.cpp index dc734a5048..ad4244bf1d 100644 --- a/ext/native/base/PCMain.cpp +++ b/ext/native/base/PCMain.cpp @@ -27,6 +27,7 @@ SDLJoystick *joystick = NULL; #endif #include +#include #include "base/display.h" #include "base/logging.h" @@ -51,6 +52,8 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); + assert(success); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ext/native/base/QtMain.h b/ext/native/base/QtMain.h index 0fda2cde4a..d1bcc87fa9 100644 --- a/ext/native/base/QtMain.h +++ b/ext/native/base/QtMain.h @@ -18,6 +18,8 @@ QTM_USE_NAMESPACE #endif #endif +#include + #include "base/display.h" #include "base/logging.h" #include "base/timeutil.h" @@ -42,6 +44,8 @@ class QtDummyGraphicsContext : public DummyGraphicsContext { public: QtDummyGraphicsContext() { draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); + assert(success); } ~QtDummyGraphicsContext() { delete draw_; diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 9e229008f0..e01c9a5bb3 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -46,7 +46,7 @@ bool TextDrawerAndroid::IsReady() const { uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { // We will only use the default font but just for consistency let's still involve // the font name. - uint32_t fontHash = hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + uint32_t fontHash = hash::Adler32((const uint8_t *)fontName, strlen(fontName)); fontHash ^= size; fontHash ^= flags << 10; @@ -79,7 +79,7 @@ std::string TextDrawerAndroid::NormalizeString(std::string str) { } void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, len); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, len); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -129,7 +129,7 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou float total_w = 0.0f; float total_h = 0.0f; for (size_t i = 0; i < lines.size(); i++) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)&lines[i][0], lines[i].length()); + uint32_t stringHash = hash::Adler32((const uint8_t *)&lines[i][0], lines[i].length()); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -168,7 +168,7 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, int result = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); assert(env == env_); - uint32_t stringHash = hash::Fletcher((const uint8_t *)text.data(), text.size()); + uint32_t stringHash = hash::Adler32((const uint8_t *)text.data(), text.size()); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index d0d8773dd8..7b523f47ab 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -24,7 +24,7 @@ TextDrawerQt::~TextDrawerQt() { uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) { // We will only use the default font - uint32_t fontHash = 0; //hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + uint32_t fontHash = 0; //hash::Adler32((const uint8_t *)fontName, strlen(fontName)); fontHash ^= size; fontHash ^= flags << 10; @@ -72,7 +72,7 @@ void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, floa if (!strlen(str)) return; - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, strlen(str)); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/gfx_es2/draw_text_win.cpp b/ext/native/gfx_es2/draw_text_win.cpp index 9df6ee09d0..4f0dd44e19 100644 --- a/ext/native/gfx_es2/draw_text_win.cpp +++ b/ext/native/gfx_es2/draw_text_win.cpp @@ -83,7 +83,7 @@ TextDrawerWin32::~TextDrawerWin32() { } uint32_t TextDrawerWin32::SetFont(const char *fontName, int size, int flags) { - uint32_t fontHash = fontName ? hash::Fletcher((const uint8_t *)fontName, strlen(fontName)) : 0; + uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0; fontHash ^= size; fontHash ^= flags << 10; @@ -119,7 +119,7 @@ void TextDrawerWin32::SetFont(uint32_t fontHandle) { } void TextDrawerWin32::MeasureString(const char *str, size_t len, float *w, float *h) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, len); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, len); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -164,7 +164,7 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound float total_w = 0.0f; float total_h = 0.0f; for (size_t i = 0; i < lines.size(); i++) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)&lines[i][0], lines[i].length()); + uint32_t stringHash = hash::Adler32((const uint8_t *)&lines[i][0], lines[i].length()); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -197,7 +197,7 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f if (!strlen(str)) return; - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, strlen(str)); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index aa96d648ca..56711f541a 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -492,6 +492,7 @@ static const char *glsl_fragment_prelude = "#endif\n"; std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) { +#if !PPSSPP_PLATFORM(UWP) std::string temp; std::string version = ""; if (!gl_extensions.IsGLES && gl_extensions.IsCoreContext) { @@ -512,4 +513,7 @@ std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) { temp = version + source; } return temp; +#else + return source; +#endif } diff --git a/ext/native/math/dataconv.h b/ext/native/math/dataconv.h index 2801a06a40..84e9da1a7e 100644 --- a/ext/native/math/dataconv.h +++ b/ext/native/math/dataconv.h @@ -105,6 +105,17 @@ inline void CopyFloat3(float dest[3], const float src[3]) { dest[2] = src[2]; } +inline void CopyFloat4(float dest[3], const float src[3]) { +#ifdef _M_SSE + _mm_storeu_ps(dest, _mm_loadu_ps(src)); +#else + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +#endif +} + inline void CopyFloat1To4(float dest[4], const float src) { #ifdef _M_SSE _mm_storeu_ps(dest, _mm_set_ss(src)); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index ea47f94dea..aa3b268a19 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -240,6 +240,9 @@ + + + @@ -697,6 +700,8 @@ + + @@ -756,4 +761,4 @@ - + \ No newline at end of file diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index e551ed8b0e..6996479a53 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -320,6 +320,15 @@ gfx + + thin3d + + + thin3d + + + thin3d + @@ -781,6 +790,12 @@ math + + thin3d + + + thin3d + @@ -853,4 +868,4 @@ {06c6305a-a646-485b-85b9-645a24dd6553} - + \ No newline at end of file diff --git a/ext/native/profiler/profiler.h b/ext/native/profiler/profiler.h index e4cd55d255..f615e2e88c 100644 --- a/ext/native/profiler/profiler.h +++ b/ext/native/profiler/profiler.h @@ -2,6 +2,8 @@ #include +// NOTE : This profiler is very single-threaded. Cannot be used from multiple threads yet. + // #define USE_PROFILER #ifdef USE_PROFILER @@ -14,7 +16,6 @@ void internal_profiler_end_frame(); int internal_profiler_enter(const char *category_name); // Returns the category number. void internal_profiler_leave(int category); - const char *Profiler_GetCategoryName(int i); int Profiler_GetNumCategories(); int Profiler_GetHistoryLength(); diff --git a/ext/native/thin3d/DataFormat.h b/ext/native/thin3d/DataFormat.h new file mode 100644 index 0000000000..fa9a8325be --- /dev/null +++ b/ext/native/thin3d/DataFormat.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +namespace Draw { + +enum class DataFormat : uint8_t { + UNDEFINED, + + R8_UNORM, + R8G8_UNORM, + R8G8B8_UNORM, + + R8G8B8A8_UNORM, + R8G8B8A8_UNORM_SRGB, + B8G8R8A8_UNORM, // D3D style + B8G8R8A8_UNORM_SRGB, // D3D style + + R8G8B8A8_SNORM, + R8G8B8A8_UINT, + R8G8B8A8_SINT, + + R4G4_UNORM_PACK8, + A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit + B4G4R4A4_UNORM_PACK16, + R4G4B4A4_UNORM_PACK16, + R5G6B5_UNORM_PACK16, + B5G6R5_UNORM_PACK16, + R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit + B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit + A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit. + + R16_FLOAT, + R16G16_FLOAT, + R16G16B16A16_FLOAT, + + R32_FLOAT, + R32G32_FLOAT, + R32G32B32_FLOAT, + R32G32B32A32_FLOAT, + + // Block compression formats. + // These are modern names for DXT and friends, now patent free. + // https://msdn.microsoft.com/en-us/library/bb694531.aspx + BC1_RGBA_UNORM_BLOCK, + BC1_RGBA_SRGB_BLOCK, + BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using + BC2_SRGB_BLOCK, + BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color + BC3_SRGB_BLOCK, + BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha + BC4_SNORM_BLOCK, + BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha + BC5_SNORM_BLOCK, + BC6H_UFLOAT_BLOCK, // TODO + BC6H_SFLOAT_BLOCK, + BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality. + BC7_SRGB_BLOCK, + + ETC1, + + S8, + D16, + D24_S8, + D32F, + D32F_S8, +}; + +void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); + +} // namespace diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp new file mode 100644 index 0000000000..83511ecca9 --- /dev/null +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -0,0 +1,842 @@ +#include "DataFormat.h" +#include "VulkanQueueRunner.h" +#include "VulkanRenderManager.h" + +const uint32_t readbackBufferSize = 2048 * 2048 * 4; + +void VulkanQueueRunner::CreateDeviceObjects() { + InitBackbufferRenderPass(); + InitRenderpasses(); + + VkDevice device = vulkan_->GetDevice(); + + VkBufferCreateInfo buf{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + buf.size = readbackBufferSize; + buf.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + vkCreateBuffer(device, &buf, nullptr, &readbackBuffer_); + + VkMemoryRequirements reqs{}; + vkGetBufferMemoryRequirements(device, readbackBuffer_, &reqs); + + VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc.allocationSize = reqs.size; + + VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &alloc.memoryTypeIndex); + assert(success); + vkAllocateMemory(device, &alloc, nullptr, &readbackMemory_); + + uint32_t offset = 0; + vkBindBufferMemory(device, readbackBuffer_, readbackMemory_, offset); +} + +void VulkanQueueRunner::DestroyDeviceObjects() { + VkDevice device = vulkan_->GetDevice(); + vkFreeMemory(device, readbackMemory_, nullptr); + vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_); + + for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + assert(renderPasses_[i] != VK_NULL_HANDLE); + vkDestroyRenderPass(device, renderPasses_[i], nullptr); + } + assert(backbufferRenderPass_ != VK_NULL_HANDLE); + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); +} + +void VulkanQueueRunner::InitBackbufferRenderPass() { + VkResult U_ASSERT_ONLY res; + + VkAttachmentDescription attachments[2]; + attachments[0].format = vulkan_->GetSwapchainFormat(); + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + 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_UNDEFINED; // We don't want to preserve the backbuffer between frames so we really don't care. + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // We only render once to the backbuffer per frame so we can do this here. + attachments[0].flags = 0; + + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Don't care about storing backbuffer Z - we clear it anyway. + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + 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 = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + // For the built-in layout transitions. + VkSubpassDependency dep{}; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp_info.attachmentCount = 2; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 1; + rp_info.pDependencies = &dep; + + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); + assert(res == VK_SUCCESS); +} + +void VulkanQueueRunner::InitRenderpasses() { + // Create a bunch of render pass objects, for normal rendering with a depth buffer, + // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. + VkAttachmentDescription attachments[2] = {}; + attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + 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; // TODO: Look into auto-transitioning to SAMPLED when appropriate. + 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 = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp.attachmentCount = 2; + rp.pAttachments = attachments; + rp.subpassCount = 1; + rp.pSubpasses = &subpass; + rp.dependencyCount = 0; + + for (int depth = 0; depth < 3; depth++) { + switch ((VKRRenderPassAction)depth) { + case VKRRenderPassAction::CLEAR: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case VKRRenderPassAction::KEEP: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case VKRRenderPassAction::DONT_CARE: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + } + for (int color = 0; color < 3; color++) { + switch ((VKRRenderPassAction)color) { + case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + int index = RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[index]); + } + } +} +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { + // Optimizes renderpasses, then sequences them. + for (int i = 0; i < steps.size(); i++) { + const VKRStep &step = *steps[i]; + switch (step.stepType) { + case VKRStepType::RENDER: + PerformRenderPass(step, cmd); + break; + case VKRStepType::COPY: + PerformCopy(step, cmd); + break; + case VKRStepType::BLIT: + PerformBlit(step, cmd); + break; + case VKRStepType::READBACK: + PerformReadback(step, cmd); + break; + } + delete steps[i]; + } +} + +void VulkanQueueRunner::LogSteps(const std::vector &steps) { + ILOG("======================================="); + for (int i = 0; i < steps.size(); i++) { + const VKRStep &step = *steps[i]; + switch (step.stepType) { + case VKRStepType::RENDER: + LogRenderPass(step); + break; + case VKRStepType::COPY: + LogCopy(step); + break; + case VKRStepType::BLIT: + LogBlit(step); + break; + case VKRStepType::READBACK: + LogReadback(step); + break; + } + } +} + +void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { + int fb = (int)(intptr_t)(pass.render.framebuffer ? pass.render.framebuffer->framebuf : 0); + ILOG("RenderPass Begin(%x)", fb); + for (auto &cmd : pass.commands) { + switch (cmd.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + ILOG(" BindPipeline(%x)", (int)(intptr_t)cmd.pipeline.pipeline); + break; + case VKRRenderCommand::BLEND: + ILOG(" Blend(%f, %f, %f, %f)", cmd.blendColor.color[0], cmd.blendColor.color[1], cmd.blendColor.color[2], cmd.blendColor.color[3]); + break; + case VKRRenderCommand::CLEAR: + ILOG(" Clear"); + break; + case VKRRenderCommand::DRAW: + ILOG(" Draw(%d)", cmd.draw.count); + break; + case VKRRenderCommand::DRAW_INDEXED: + ILOG(" DrawIndexed(%d)", cmd.drawIndexed.count); + break; + case VKRRenderCommand::SCISSOR: + ILOG(" Scissor(%d, %d, %d, %d)", (int)cmd.scissor.scissor.offset.x, (int)cmd.scissor.scissor.offset.y, (int)cmd.scissor.scissor.extent.width, (int)cmd.scissor.scissor.extent.height); + break; + case VKRRenderCommand::STENCIL: + ILOG(" Stencil(ref=%d, compare=%d, write=%d)", cmd.stencil.stencilRef, cmd.stencil.stencilCompareMask, cmd.stencil.stencilWriteMask); + break; + case VKRRenderCommand::VIEWPORT: + ILOG(" Viewport(%f, %f, %f, %f, %f, %f)", cmd.viewport.vp.x, cmd.viewport.vp.y, cmd.viewport.vp.width, cmd.viewport.vp.height, cmd.viewport.vp.minDepth, cmd.viewport.vp.maxDepth); + break; + case VKRRenderCommand::PUSH_CONSTANTS: + ILOG(" PushConstants(%d)", cmd.push.size); + break; + } + } + ILOG("RenderPass End(%x)", fb); +} + +void VulkanQueueRunner::LogCopy(const VKRStep &pass) { + ILOG("Copy()"); +} + +void VulkanQueueRunner::LogBlit(const VKRStep &pass) { + ILOG("Blit()"); +} + +void VulkanQueueRunner::LogReadback(const VKRStep &pass) { + ILOG("Readback"); +} + +void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + // TODO: If there are multiple, we can transition them together. + for (const auto &iter : step.preTransitions) { + if (iter.fb->color.layout != iter.targetLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = iter.fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = iter.fb->color.image; + barrier.srcAccessMask = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + barrier.newLayout = iter.targetLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + break; + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + iter.fb->color.layout = barrier.newLayout; + } + } + + // This is supposed to bind a vulkan render pass to the command buffer. + PerformBindFramebufferAsRenderTarget(step, cmd); + + int curWidth = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth(); + int curHeight = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); + + VKRFramebuffer *fb = step.render.framebuffer; + + VkPipeline lastPipeline = VK_NULL_HANDLE; + VkDescriptorSet lastDescSet = VK_NULL_HANDLE; + + auto &commands = step.commands; + + // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only + // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here + // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. + + for (const auto &c : commands) { + switch (c.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + if (c.pipeline.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); + lastPipeline = c.pipeline.pipeline; + } + break; + + case VKRRenderCommand::VIEWPORT: + vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); + break; + + case VKRRenderCommand::SCISSOR: + vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); + break; + + case VKRRenderCommand::BLEND: + vkCmdSetBlendConstants(cmd, c.blendColor.color); + break; + + case VKRRenderCommand::PUSH_CONSTANTS: + vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); + break; + + case VKRRenderCommand::STENCIL: + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; + + case VKRRenderCommand::DRAW_INDEXED: + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; + + case VKRRenderCommand::DRAW: + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + if (c.draw.vbuffer) { + vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + } + vkCmdDraw(cmd, c.draw.count, 1, 0, 0); + break; + + case VKRRenderCommand::CLEAR: + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth; + rc.rect.extent.height = curHeight; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + if (numAttachments) { + vkCmdClearAttachments(cmd, numAttachments, attachments, 1, &rc); + } + break; + } + default: + ELOG("Unimpl queue command"); + ; + } + } + vkCmdEndRenderPass(cmd); + + // Transition the framebuffer if requested. + if (fb && step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + default: + Crash(); + } + barrier.newLayout = step.render.finalColorLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + // we're between passes so it's OK. + // ARM Best Practices guide recommends these stage bits. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } +} + +void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { + VkFramebuffer framebuf; + int w; + int h; + VkImageLayout prevLayout; + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + framebuf = fb->framebuf; + w = fb->width; + h = fb->height; + prevLayout = fb->color.layout; + } else { + framebuf = backbuffer_; + w = vulkan_->GetBackbufferWidth(); + h = vulkan_->GetBackbufferHeight(); + } + + VkRenderPass renderPass; + int numClearVals = 0; + VkClearValue clearVal[2]; + memset(clearVal, 0, sizeof(clearVal)); + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + // Now, if the image needs transitioning, let's transition. + // The backbuffer does not, that's handled by VulkanContext. + if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->depth.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + TransitionImageLayout2(cmd, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); + fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; + if (step.render.color == VKRRenderPassAction::CLEAR) { + Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); + numClearVals = 1; + } + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { + clearVal[1].depthStencil.depth = step.render.clearDepth; + clearVal[1].depthStencil.stencil = step.render.clearStencil; + numClearVals = 2; + } + } else { + renderPass = GetBackbufferRenderPass(); + numClearVals = 2; // We don't bother with a depth buffer here. + clearVal[1].depthStencil.depth = 0.0f; + clearVal[1].depthStencil.stencil = 0; + } + + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = renderPass; + rp_begin.framebuffer = framebuf; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = w; + rp_begin.renderArea.extent.height = h; + rp_begin.clearValueCount = numClearVals; + rp_begin.pClearValues = numClearVals ? clearVal : nullptr; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); +} + +void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { + VKRFramebuffer *src = step.copy.src; + VKRFramebuffer *dst = step.copy.dst; + + VkImageCopy copy{}; + copy.srcOffset.x = step.copy.srcRect.offset.x; + copy.srcOffset.y = step.copy.srcRect.offset.y; + copy.srcOffset.z = 0; + copy.srcSubresource.mipLevel = 0; + copy.srcSubresource.layerCount = 1; + copy.dstOffset.x = step.copy.dstPos.x; + copy.dstOffset.y = step.copy.dstPos.y; + copy.dstOffset.z = 0; + copy.dstSubresource.mipLevel = 0; + copy.dstSubresource.layerCount = 1; + copy.extent.width = step.copy.srcRect.extent.width; + copy.extent.height = step.copy.srcRect.extent.height; + copy.extent.depth = 1; + + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + int srcCount = 0; + int dstCount = 0; + + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; + // First source barriers. + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + if (srcCount) { + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); + } + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + copy.srcSubresource.aspectMask = 0; + copy.dstSubresource.aspectMask = 0; + if (step.copy.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.copy.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); + } +} + +void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + + VKRFramebuffer *src = step.blit.src; + VKRFramebuffer *dst = step.blit.dst; + + // If any validation needs to be performed here, it should probably have been done + // already when the blit was queued. So don't validate here. + VkImageBlit blit{}; + blit.srcOffsets[0].x = step.blit.srcRect.offset.x; + blit.srcOffsets[0].y = step.blit.srcRect.offset.y; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = step.blit.srcRect.offset.x + step.blit.srcRect.extent.width; + blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height; + blit.srcOffsets[1].z = 1; + blit.srcSubresource.mipLevel = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0].x = step.blit.dstRect.offset.x; + blit.dstOffsets[0].y = step.blit.dstRect.offset.y; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = step.blit.dstRect.offset.x + step.blit.dstRect.extent.width; + blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height; + blit.dstOffsets[1].z = 1; + blit.dstSubresource.mipLevel = 0; + blit.dstSubresource.layerCount = 1; + + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; + + int srcCount = 0; + int dstCount = 0; + + // First source barriers. + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + if (srcCount) { + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); + } + + // TODO: Need to check if the depth format is blittable. + // Actually, we should probably almost always use copies rather than blits for depth buffers. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + blit.srcSubresource.aspectMask = 0; + blit.dstSubresource.aspectMask = 0; + if (step.blit.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.blit.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, step.blit.filter); + } +} + +void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img.layout = barrier.newLayout; +} + +void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img.layout = barrier.newLayout; +} + +void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) { + VKRImage *srcImage; + if (step.readback.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + srcImage = &step.readback.src->color; + } else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + srcImage = &step.readback.src->depth; + } else { + assert(false); + } + + assert(srcImage->format == VK_FORMAT_R8G8B8A8_UNORM); + + VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + VkPipelineStageFlags stage = 0; + SetupTransitionToTransferSrc(*srcImage, barrier, stage, step.readback.aspectMask); + vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + + VkBufferImageCopy region{}; + region.imageOffset = { step.readback.srcRect.offset.x, step.readback.srcRect.offset.y, 0 }; + region.imageExtent = { step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, 1 }; + region.imageSubresource.aspectMask = step.readback.aspectMask; + region.imageSubresource.layerCount = 1; + region.bufferOffset = 0; + region.bufferRowLength = step.readback.srcRect.extent.width; + region.bufferImageHeight = step.readback.srcRect.extent.height; + vkCmdCopyImageToBuffer(cmd, srcImage->image, srcImage->layout, readbackBuffer_, 1, ®ion); + + // NOTE: Can't read the buffer using the CPU here - need to sync first. +} + +void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { + // Read back to the requested address in ram from buffer. + void *mappedData; + const int srcPixelSize = 4; // TODO: Fix. + + VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * srcPixelSize, 0, &mappedData); + assert(res == VK_SUCCESS); + ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat); + vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_); +} diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h new file mode 100644 index 0000000000..06a85a0721 --- /dev/null +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -0,0 +1,196 @@ +#pragma once + +#include + +#include "Common/Vulkan/VulkanContext.h" +#include "math/dataconv.h" +#include "thin3d/DataFormat.h" + +class VKRFramebuffer; +struct VKRImage; + +enum class VKRRenderCommand : uint8_t { + BIND_PIPELINE, + STENCIL, + BLEND, + VIEWPORT, + SCISSOR, + CLEAR, + DRAW, + DRAW_INDEXED, + PUSH_CONSTANTS, +}; + +struct VkRenderData { + VKRRenderCommand cmd; + union { + struct { + VkPipeline pipeline; + } pipeline; + struct { + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; + VkDeviceSize voffset; + uint32_t count; + } draw; + struct { + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; // might need to increase at some point + VkDeviceSize voffset; + VkBuffer ibuffer; + VkDeviceSize ioffset; + uint32_t count; + int16_t instances; + VkIndexType indexType; + } drawIndexed; + struct { + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + } clear; + struct { + VkViewport vp; + } viewport; + struct { + VkRect2D scissor; + } scissor; + struct { + uint8_t stencilWriteMask; + uint8_t stencilCompareMask; + uint8_t stencilRef; + } stencil; + struct { + float color[4]; + } blendColor; + struct { + VkPipelineLayout pipelineLayout; + VkShaderStageFlags stages; + uint8_t offset; + uint8_t size; + uint8_t data[32]; // Should be enough for now. + } push; + }; +}; + +enum class VKRStepType : uint8_t { + RENDER, + COPY, + BLIT, + READBACK, +}; + +enum class VKRRenderPassAction { + DONT_CARE, + CLEAR, + KEEP, +}; + +struct TransitionRequest { + VKRFramebuffer *fb; + VkImageLayout targetLayout; +}; + +struct VKRStep { + VKRStep(VKRStepType _type) : stepType(_type) {} + VKRStepType stepType; + std::vector commands; + std::vector preTransitions; + union { + struct { + VKRFramebuffer *framebuffer; + VKRRenderPassAction color; + VKRRenderPassAction depthStencil; + uint32_t clearColor; + float clearDepth; + int clearStencil; + int numDraws; + VkImageLayout finalColorLayout; + } render; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkOffset2D dstPos; + int aspectMask; + } copy; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkRect2D dstRect; + int aspectMask; + VkFilter filter; + } blit; + struct { + int aspectMask; + VKRFramebuffer *src; + VkRect2D srcRect; + } readback; + }; +}; + +class VulkanQueueRunner { +public: + VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan) {} + void SetBackbuffer(VkFramebuffer fb) { + backbuffer_ = fb; + } + void RunSteps(VkCommandBuffer cmd, const std::vector &steps); + void LogSteps(const std::vector &steps); + + void CreateDeviceObjects(); + void DestroyDeviceObjects(); + + VkRenderPass GetBackbufferRenderPass() const { + return backbufferRenderPass_; + } + VkRenderPass GetRenderPass(int i) const { + return renderPasses_[i]; + } + + inline int RPIndex(VKRRenderPassAction color, VKRRenderPassAction depth) { + return (int)depth * 3 + (int)color; + } + + void CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); + +private: + void InitBackbufferRenderPass(); + void InitRenderpasses(); + + void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); + void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); + void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); + void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd); + + void LogRenderPass(const VKRStep &pass); + void LogCopy(const VKRStep &pass); + void LogBlit(const VKRStep &pass); + void LogReadback(const VKRStep &pass); + + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + + VulkanContext *vulkan_; + + VkFramebuffer backbuffer_; + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. + // TODO: Create these on demand. + VkRenderPass renderPasses_[9]{}; + + // Readback buffer. Currently we only support synchronous readback, so we only really need one. + // We size it generously. + VkDeviceMemory readbackMemory_; + VkBuffer readbackBuffer_; +}; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp new file mode 100644 index 0000000000..1d67e3fe49 --- /dev/null +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -0,0 +1,719 @@ +#include +#include "base/logging.h" + +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/VulkanRenderManager.h" +#include "thread/threadutil.h" + +#if 0 // def _DEBUG +#define VLOG ILOG +#else +#define VLOG(...) +#endif + +// TODO: Using a thread here is unfinished and does not work correctly. +const bool useThread = false; + +#ifndef UINT64_MAX +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + + +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { + VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + ici.arrayLayers = 1; + ici.mipLevels = 1; + ici.extent.width = width; + ici.extent.height = height; + ici.extent.depth = 1; + ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ici.imageType = VK_IMAGE_TYPE_2D; + ici.samples = VK_SAMPLE_COUNT_1_BIT; + ici.tiling = VK_IMAGE_TILING_OPTIMAL; + ici.format = format; + // Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers. + ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (color) { + ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } else { + ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + + vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); + + // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers + + VkMemoryRequirements memreq; + vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); + + VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc.allocationSize = memreq.size; + vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); + VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); + assert(res == VK_SUCCESS); + res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); + assert(res == VK_SUCCESS); + + VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + + VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + ivci.format = ici.format; + ivci.image = img.image; + ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; + ivci.subresourceRange.aspectMask = aspects; + ivci.subresourceRange.layerCount = 1; + ivci.subresourceRange.levelCount = 1; + res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView); + assert(res == VK_SUCCESS); + + VkPipelineStageFlags dstStage; + VkAccessFlagBits dstAccessMask; + switch (initialLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + default: + Crash(); + break; + } + + TransitionImageLayout2(cmd, img.image, aspects, + VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, + 0, dstAccessMask); + img.layout = initialLayout; + + img.format = format; +} + +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(vulkan) { + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + semaphoreCreateInfo.flags = 0; + VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore_); + assert(res == VK_SUCCESS); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); + assert(res == VK_SUCCESS); + + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolInit); + assert(res == VK_SUCCESS); + res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolMain); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cmd_alloc.commandPool = frameData_[i].cmdPoolInit; + cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_alloc.commandBufferCount = 1; + + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].initCmd); + assert(res == VK_SUCCESS); + cmd_alloc.commandPool = frameData_[i].cmdPoolMain; + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd); + assert(res == VK_SUCCESS); + frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on + } + + queueRunner_.CreateDeviceObjects(); +} + +void VulkanRenderManager::CreateBackbuffers() { + VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); + assert(res == VK_SUCCESS); + + VkImage* swapchainImages = new VkImage[swapchainImageCount_]; + res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); + assert(res == VK_SUCCESS); + + VkCommandBuffer cmdInit = GetInitCmd(); + + for (uint32_t i = 0; i < swapchainImageCount_; i++) { + SwapchainImageData sc_buffer; + sc_buffer.image = swapchainImages[i]; + + VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + color_image_view.format = vulkan_->GetSwapchainFormat(); + color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; + color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; + color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; + color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; + color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_image_view.subresourceRange.baseMipLevel = 0; + color_image_view.subresourceRange.levelCount = 1; + color_image_view.subresourceRange.baseArrayLayer = 0; + color_image_view.subresourceRange.layerCount = 1; + color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + color_image_view.flags = 0; + color_image_view.image = sc_buffer.image; + + // Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring + // in image to render to will be to transition them away from that. + TransitionImageLayout2(cmdInit, sc_buffer.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, nullptr, &sc_buffer.view); + swapchainImages_.push_back(sc_buffer); + assert(res == VK_SUCCESS); + } + delete[] swapchainImages; + + InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. + InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + curWidth_ = -1; + curHeight_ = -1; + + // Start the thread. + if (useThread) { + run_ = true; + thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); + } +} + +void VulkanRenderManager::DestroyBackbuffers() { + if (useThread) { + run_ = false; + // Stop the thread. + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + { + std::unique_lock lock(frameData_[i].push_mutex); + frameData_[i].push_condVar.notify_all(); + } + { + std::unique_lock lock(frameData_[i].pull_mutex); + frameData_[i].pull_condVar.notify_all(); + } + } + thread_.join(); + } + VkDevice device = vulkan_->GetDevice(); + for (uint32_t i = 0; i < swapchainImageCount_; i++) { + vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); + } + vulkan_->Delete().QueueDeleteImageView(depth_.view); + vulkan_->Delete().QueueDeleteImage(depth_.image); + vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem); + for (uint32_t i = 0; i < framebuffers_.size(); i++) { + assert(framebuffers_[i] != VK_NULL_HANDLE); + vulkan_->Delete().QueueDeleteFramebuffer(framebuffers_[i]); + } + framebuffers_.clear(); + + swapchainImages_.clear(); +} + +VulkanRenderManager::~VulkanRenderManager() { + run_ = false; + VkDevice device = vulkan_->GetDevice(); + vulkan_->WaitUntilQueueIdle(); + vkDestroySemaphore(device, acquireSemaphore_, nullptr); + vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr); + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; + vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); + vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); + vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); + vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); + vkDestroyFence(device, frameData_[i].fence, nullptr); + } + queueRunner_.DestroyDeviceObjects(); +} + +// TODO: Activate this code. +void VulkanRenderManager::ThreadFunc() { + setCurrentThreadName("RenderMan"); + int threadFrame = -1; // Increment first, start at 0. + while (run_) { + { + threadFrame++; + if (threadFrame >= vulkan_->GetInflightFrames()) + threadFrame = 0; + FrameData &frameData = frameData_[threadFrame]; + std::unique_lock lock(frameData.pull_mutex); + while (!frameData.readyForRun && run_) { + VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + frameData.pull_condVar.wait(lock); + } + VLOG("PULL: frame[%d].readyForRun = false", threadFrame); + frameData.readyForRun = false; + if (!run_) // quick exit if bailing. + return; + } + VLOG("PULL: Running frame %d", threadFrame); + Run(threadFrame); + } +} + +void VulkanRenderManager::BeginFrame() { + VkDevice device = vulkan_->GetDevice(); + + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + + // Make sure the very last command buffer from the frame before the previous has been fully executed. + if (useThread) { + std::unique_lock lock(frameData.push_mutex); + while (!frameData.readyForFence) { + VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + frameData.push_condVar.wait(lock); + } + frameData.readyForFence = false; + } + + VLOG("PUSH: Fencing %d", curFrame); + vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); + vkResetFences(device, 1, &frameData.fence); + + // Must be after the fence - this performs deletes. + VLOG("PUSH: BeginFrame %d", curFrame); + vulkan_->BeginFrame(); + + insideFrame_ = true; +} + +VkCommandBuffer VulkanRenderManager::GetInitCmd() { + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (!frameData.hasInitCommands) { + VkCommandBufferBeginInfo begin = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); + assert(res == VK_SUCCESS); + frameData.hasInitCommands = true; + } + return frameData_[curFrame].initCmd; +} + +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + assert(insideFrame_); + // Eliminate dupes. + if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) { + if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { + // We don't move to a new step, this bind was unnecessary and we can safely skip it. + return; + } + } + if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == VKRRenderPassAction::KEEP) { + // Can trivially kill the last empty render step. + assert(steps_.back() == curRenderStep_); + delete steps_.back(); + steps_.pop_back(); + curRenderStep_ = nullptr; + } + if (curRenderStep_ && curRenderStep_->commands.size() == 0) { + ILOG("Empty render step. Usually happens after uploading pixels.."); + } + + VKRStep *step = new VKRStep{ VKRStepType::RENDER }; + // This is what queues up new passes, and can end previous ones. + step->render.framebuffer = fb; + step->render.color = color; + step->render.depthStencil = depth; + step->render.clearColor = clearColor; + step->render.clearDepth = clearDepth; + step->render.clearStencil = clearStencil; + step->render.numDraws = 0; + step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + steps_.push_back(step); + + curRenderStep_ = step; + curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); + curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); +} + +void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { + VKRStep *step = new VKRStep{ VKRStepType::READBACK }; + step->readback.aspectMask = aspectBits; + step->readback.src = src; + step->readback.srcRect.offset = { x, y }; + step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h }; + steps_.push_back(step); + + curRenderStep_ = nullptr; + + FlushSync(); + + // Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM. + queueRunner_.CopyReadbackBuffer(w, h, destFormat, pixelStride, pixels); +} + +void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { + VkResult U_ASSERT_ONLY res; + // We share the same depth buffer but have multiple color buffers, see the loop below. + VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; + + VLOG("InitFramebuffers: %dx%d", width, height); + VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + fb_info.renderPass = queueRunner_.GetBackbufferRenderPass(); + fb_info.attachmentCount = 2; + fb_info.pAttachments = attachments; + fb_info.width = width; + fb_info.height = height; + fb_info.layers = 1; + + framebuffers_.resize(swapchainImageCount_); + + for (uint32_t i = 0; i < swapchainImageCount_; i++) { + attachments[0] = swapchainImages_[i].view; + res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); + assert(res == VK_SUCCESS); + } +} + +void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { + VkResult U_ASSERT_ONLY res; + bool U_ASSERT_ONLY pass; + + const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = depth_format; + image_info.extent.width = vulkan_->GetBackbufferWidth(); + image_info.extent.height = vulkan_->GetBackbufferHeight(); + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image_info.flags = 0; + + VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkMemoryRequirements mem_reqs; + + depth_.format = depth_format; + + VkDevice device = vulkan_->GetDevice(); + res = vkCreateImage(device, &image_info, NULL, &depth_.image); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(device, depth_.image, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + // Use the memory properties to determine the type of memory required + pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, + 0, /* No requirements */ + &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(device, &mem_alloc, NULL, &depth_.mem); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(device, depth_.image, depth_.mem, 0); + assert(res == VK_SUCCESS); + + TransitionImageLayout2(cmd, depth_.image, + aspectMask, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + + VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + depth_view_info.image = depth_.image; + depth_view_info.format = depth_format; + depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; + depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; + depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; + depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; + depth_view_info.subresourceRange.aspectMask = aspectMask; + depth_view_info.subresourceRange.baseMipLevel = 0; + depth_view_info.subresourceRange.levelCount = 1; + depth_view_info.subresourceRange.baseArrayLayer = 0; + depth_view_info.subresourceRange.layerCount = 1; + depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + depth_view_info.flags = 0; + + res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view); + assert(res == VK_SUCCESS); +} + +void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + // If this is the first drawing command, merge it into the pass. + if (curRenderStep_->render.numDraws == 0) { + curRenderStep_->render.clearColor = clearColor; + curRenderStep_->render.clearDepth = clearZ; + curRenderStep_->render.clearStencil = clearStencil; + curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; + curRenderStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; + } else { + VkRenderData data{ VKRRenderCommand::CLEAR }; + data.clear.clearColor = clearColor; + data.clear.clearZ = clearZ; + data.clear.clearStencil = clearStencil; + data.clear.clearMask = clearMask; + curRenderStep_->commands.push_back(data); + } +} + +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0"); + _dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0"); + _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= (uint32_t)dst->width, "dstPos + extent x > width"); + _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= (uint32_t)dst->height, "dstPos + extent y > height"); + + VKRStep *step = new VKRStep{ VKRStepType::COPY }; + + step->copy.aspectMask = aspectMask; + step->copy.src = src; + step->copy.srcRect = srcRect; + step->copy.dst = dst; + step->copy.dstPos = dstPos; + + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; +} + +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); + + _dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= (uint32_t)dst->width, "dstrect offset x + extent > width"); + _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= (uint32_t)dst->height, "dstrect offset y + extent > height"); + + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; + + step->blit.aspectMask = aspectMask; + step->blit.src = src; + step->blit.srcRect = srcRect; + step->blit.dst = dst; + step->blit.dstRect = dstRect; + step->blit.filter = filter; + + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; +} + +VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { + // Should just mark the dependency and return the image. + for (int i = (int)steps_.size() - 1; i >= 0; i--) { + if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { + // If this framebuffer was rendered to earlier in this frame, make sure to pre-transition it to the correct layout. + if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + break; + } + else if (steps_[i]->render.finalColorLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + Crash(); + // May need to shadow the framebuffer if we re-order passes later. + } + } + } + + curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); + return fb->color.imageView; +} + +void VulkanRenderManager::Finish() { + curRenderStep_ = nullptr; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (!useThread) { + frameData.steps = std::move(steps_); + Run(curFrame); + } else { + std::unique_lock lock(frameData.pull_mutex); + VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); + frameData.steps = std::move(steps_); + frameData.readyForRun = true; + frameData.pull_condVar.notify_all(); + } + vulkan_->EndFrame(); +} + +void VulkanRenderManager::Wipe() { + int curFrame = vulkan_->GetCurFrame(); + for (auto iter : steps_) { + delete iter; + } + steps_.clear(); +} + +// Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, +// or stop it in the middle for a synchronous readback, then start over again mostly normally but without repeating +// the backbuffer image acquisition. +void VulkanRenderManager::BeginSubmitFrame(int frame) { + FrameData &frameData = frameData_[frame]; + if (!frameData.hasBegun) { + // 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(vulkan_->GetDevice(), vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &frameData.curSwapchainImage); + assert(res == VK_SUCCESS); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR + // return codes + + VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + + assert(res == VK_SUCCESS); + + queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage]); + + frameData.hasBegun = true; + } +} + +void VulkanRenderManager::Submit(int frame, bool triggerFence) { + FrameData &frameData = frameData_[frame]; + if (frameData.hasInitCommands) { + VkResult res = vkEndCommandBuffer(frameData.initCmd); + assert(res == VK_SUCCESS); + } + + VkResult res = vkEndCommandBuffer(frameData.mainCmd); + assert(res == VK_SUCCESS); + + VkCommandBuffer cmdBufs[2]; + int numCmdBufs = 0; + if (frameData.hasInitCommands) { + cmdBufs[numCmdBufs++] = frameData.initCmd; + frameData.hasInitCommands = false; + } + if (false) { + // Send the init commands off separately. Used this once to confirm that the cause of a device loss was in the init cmdbuf. + VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = (uint32_t)numCmdBufs; + submit_info.pCommandBuffers = cmdBufs; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, VK_NULL_HANDLE); + if (res == VK_ERROR_DEVICE_LOST) { + _assert_msg_(G3D, false, "Lost the Vulkan device!"); + } else { + assert(res == VK_SUCCESS); + } + numCmdBufs = 0; + } + cmdBufs[numCmdBufs++] = frameData.mainCmd; + + VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; + if (triggerFence) { + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submit_info.pWaitDstStageMask = waitStage; + } + submit_info.commandBufferCount = (uint32_t)numCmdBufs; + submit_info.pCommandBuffers = cmdBufs; + if (triggerFence) { + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; + } + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE); + if (res == VK_ERROR_DEVICE_LOST) { + _assert_msg_(G3D, false, "Lost the Vulkan device!"); + } else { + assert(res == VK_SUCCESS); + } + + if (useThread) { + VLOG("PULL: Frame %d.readyForFence = true", frame); + std::unique_lock lock(frameData.push_mutex); + frameData.readyForFence = true; + frameData.push_condVar.notify_all(); + } +} + +void VulkanRenderManager::EndSubmitFrame(int frame) { + FrameData &frameData = frameData_[frame]; + frameData.hasBegun = false; + insideFrame_ = false; + + Submit(frame, true); + + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present.swapchainCount = 1; + present.pSwapchains = &swapchain; + present.pImageIndices = &frameData.curSwapchainImage; + present.pWaitSemaphores = &renderingCompleteSemaphore_; + present.waitSemaphoreCount = 1; + + VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI + // return codes + if (res == VK_ERROR_OUT_OF_DATE_KHR) { + // ignore, it'll be fine. this happens sometimes during resizes, and we do make sure to recreate the swap chain. + } else { + assert(res == VK_SUCCESS); + } +} + +void VulkanRenderManager::Run(int frame) { + VkDevice device = vulkan_->GetDevice(); + + BeginSubmitFrame(frame); + + FrameData &frameData = frameData_[frame]; + auto &stepsOnThread = frameData_[frame].steps; + VkCommandBuffer cmd = frameData.mainCmd; + // queueRunner_.LogSteps(stepsOnThread); + queueRunner_.RunSteps(cmd, stepsOnThread); + stepsOnThread.clear(); + + EndSubmitFrame(frame); + + VLOG("PULL: Finished running frame %d", frame); +} + +void VulkanRenderManager::FlushSync() { + int frame = vulkan_->GetCurFrame(); + BeginSubmitFrame(frame); + + FrameData &frameData = frameData_[frame]; + frameData.steps = std::move(steps_); + + auto &stepsOnThread = frameData_[frame].steps; + VkCommandBuffer cmd = frameData.mainCmd; + queueRunner_.RunSteps(cmd, stepsOnThread); + stepsOnThread.clear(); + + Submit(frame, false); + + // This is brutal! Should probably wait for a fence instead, not that it'll matter much since we'll + // still stall everything. + vkDeviceWaitIdle(vulkan_->GetDevice()); + + // At this point we can resume filling the command buffers for the current frame since + // we know the device is idle - and thus all previously enqueued command buffers have been processed. + // No need to switch to the next frame number. + VkCommandBufferBeginInfo begin{ + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + assert(res == VK_SUCCESS); +} diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h new file mode 100644 index 0000000000..44d34c6cc1 --- /dev/null +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -0,0 +1,269 @@ +#pragma once + +// VulkanRenderManager takes the role that a GL driver does of sequencing and optimizing render passes. +// Only draws and binds are handled here, resource creation and allocations are handled as normal - +// that's the nice thing with Vulkan. + +#include +#include +#include +#include + +#include "Common/Vulkan/VulkanContext.h" +#include "math/dataconv.h" +#include "thin3d/DataFormat.h" +#include "thin3d/VulkanQueueRunner.h" + +// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine +// to let them have individual non-pooled allocations. Until it's not fine. We'll see. +struct VKRImage { + VkImage image; + VkImageView imageView; + VkDeviceMemory memory; + VkImageLayout layout; + VkFormat format; +}; +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); + +class VKRFramebuffer { +public: + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height) : vulkan_(vk) { + width = _width; + height = _height; + + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); + CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); + + VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + VkImageView views[2]{}; + + fbci.renderPass = renderPass; + fbci.attachmentCount = 2; + fbci.pAttachments = views; + views[0] = color.imageView; + views[1] = depth.imageView; + fbci.width = width; + fbci.height = height; + fbci.layers = 1; + + vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf); + } + ~VKRFramebuffer() { + vulkan_->Delete().QueueDeleteImage(color.image); + vulkan_->Delete().QueueDeleteImage(depth.image); + vulkan_->Delete().QueueDeleteImageView(color.imageView); + vulkan_->Delete().QueueDeleteImageView(depth.imageView); + vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); + vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); + vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + } + + int numShadows = 1; // TODO: Support this. + + VkFramebuffer framebuf = VK_NULL_HANDLE; + VKRImage color{}; + VKRImage depth{}; + int width = 0; + int height = 0; + +private: + VulkanContext *vulkan_; +}; + +class VulkanRenderManager { +public: + VulkanRenderManager(VulkanContext *vulkan); + ~VulkanRenderManager(); + + void ThreadFunc(); + + // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. + void BeginFrame(); + // Can run on a different thread! + void Finish(); + void Run(int frame); + + // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. + void Wipe(); + + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); + VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); + void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); + + void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); + void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); + + void BindPipeline(VkPipeline pipeline) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; + data.pipeline.pipeline = pipeline; + curRenderStep_->commands.push_back(data); + } + + void SetViewport(const VkViewport &vp) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::VIEWPORT }; + data.viewport.vp = vp; + curRenderStep_->commands.push_back(data); + } + + void SetScissor(const VkRect2D &rc) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::SCISSOR }; + data.scissor.scissor = rc; + curRenderStep_->commands.push_back(data); + } + + void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::STENCIL }; + data.stencil.stencilWriteMask = writeMask; + data.stencil.stencilCompareMask = compareMask; + data.stencil.stencilRef = refValue; + curRenderStep_->commands.push_back(data); + } + + void SetBlendFactor(float color[4]) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BLEND }; + CopyFloat4(data.blendColor.color, color); + curRenderStep_->commands.push_back(data); + } + + void PushConstants(VkPipelineLayout pipelineLayout, VkShaderStageFlags stages, int offset, int size, void *constants) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + assert(size + offset < 32); + VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS }; + data.push.pipelineLayout = pipelineLayout; + data.push.stages = stages; + data.push.offset = offset; + data.push.size = size; + memcpy(data.push.data, constants, size); + curRenderStep_->commands.push_back(data); + } + + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); + + void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW }; + data.draw.count = count; + data.draw.pipelineLayout = layout; + data.draw.ds = descSet; + data.draw.vbuffer = vbuffer; + data.draw.voffset = voffset; + data.draw.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.draw.uboOffsets[i] = uboOffsets[i]; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; + } + + void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; + data.drawIndexed.count = count; + data.drawIndexed.instances = numInstances; + data.drawIndexed.pipelineLayout = layout; + data.drawIndexed.ds = descSet; + data.drawIndexed.vbuffer = vbuffer; + data.drawIndexed.voffset = voffset; + data.drawIndexed.ibuffer = ibuffer; + data.drawIndexed.ioffset = ioffset; + data.drawIndexed.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.drawIndexed.uboOffsets[i] = uboOffsets[i]; + data.drawIndexed.indexType = indexType; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; + } + + VkCommandBuffer GetInitCmd(); + + VkRenderPass GetRenderPass(int pass) const { + return queueRunner_.GetRenderPass(pass); + } + VkRenderPass GetBackbufferRenderPass() const { + return queueRunner_.GetBackbufferRenderPass(); + } + VkRenderPass GetCompatibleRenderPass() const { + if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) { + return queueRunner_.GetRenderPass(0); + } else { + return queueRunner_.GetBackbufferRenderPass(); + } + } + + void CreateBackbuffers(); + void DestroyBackbuffers(); + +private: + void InitBackbufferFramebuffers(int width, int height); + void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + void BeginSubmitFrame(int frame); + void EndSubmitFrame(int frame); + void Submit(int frame, bool triggerFence); + + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). + void FlushSync(); + + // Permanent objects + VkSemaphore acquireSemaphore_; + VkSemaphore renderingCompleteSemaphore_; + + // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. + struct FrameData { + std::mutex push_mutex; + std::condition_variable push_condVar; + + std::mutex pull_mutex; + std::condition_variable pull_condVar; + + bool readyForFence = true; + + bool readyForRun = false; + VkFence fence; + // These are on different threads so need separate pools. + VkCommandPool cmdPoolInit; + VkCommandPool cmdPoolMain; + VkCommandBuffer initCmd; + VkCommandBuffer mainCmd; + bool hasInitCommands = false; + std::vector steps; + + // Swapchain. + bool hasBegun = false; + uint32_t curSwapchainImage; + }; + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; + + // Submission time state + int curWidth_; + int curHeight_; + bool insideFrame_ = false; + VKRStep *curRenderStep_ = nullptr; + std::vector steps_; + + // Execution time state + bool run_ = true; + VulkanContext *vulkan_; + std::thread thread_; + std::mutex mutex_; + VulkanQueueRunner queueRunner_; + + // Swap chain management + struct SwapchainImageData { + VkImage image; + VkImageView view; + }; + std::vector framebuffers_; + std::vector swapchainImages_; + uint32_t swapchainImageCount_; + struct DepthBufferInfo { + VkFormat format = VK_FORMAT_UNDEFINED; + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory mem = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + }; + DepthBufferInfo depth_; +}; diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 46ddbf3c53..39bdc447bc 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -1,7 +1,9 @@ #include -#include +#include #include "base/logging.h" +#include "thin3d/thin3d.h" +#include "Common/ColorConv.h" namespace Draw { @@ -41,6 +43,20 @@ size_t DataFormatSizeInBytes(DataFormat fmt) { } } +bool DataFormatIsDepthStencil(DataFormat fmt) { + switch (fmt) { + case DataFormat::D16: + case DataFormat::D24_S8: + case DataFormat::S8: + case DataFormat::D32F: + case DataFormat::D32F_S8: + return true; + default: + return false; + } +} + + bool RefCountedObject::Release() { if (refcount_ > 0 && refcount_ < 10000) { refcount_--; @@ -285,12 +301,14 @@ static ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const st return nullptr; } -void DrawContext::CreatePresets() { +bool DrawContext::CreatePresets() { vsPresets_[VS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsTexCol); vsPresets_[VS_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsCol); fsPresets_[FS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsTexCol); fsPresets_[FS_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsCol); + + return vsPresets_[VS_TEXTURE_COLOR_2D] && vsPresets_[VS_COLOR_2D] && fsPresets_[FS_TEXTURE_COLOR_2D] && fsPresets_[FS_COLOR_2D]; } DrawContext::~DrawContext() { @@ -304,4 +322,55 @@ DrawContext::~DrawContext() { } } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const uint32_t *src32 = (const uint32_t *)src; + + if (format == Draw::DataFormat::R8G8B8A8_UNORM) { + uint32_t *dst32 = (uint32_t *)dst; + if (src == dst) { + return; + } else { + for (uint32_t y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + uint16_t *dst16 = (uint16_t *)dst; + switch (format) { + case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::R8G8B8A8_UNORM: + case Draw::DataFormat::UNDEFINED: + // Not possible. + break; + } + } +} + + } // namespace Draw diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index a8749940d4..bd1a25d99a 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -12,6 +12,7 @@ #include #include "base/logging.h" +#include "DataFormat.h" class Matrix4x4; @@ -176,68 +177,6 @@ enum class TextureType : uint8_t { ARRAY2D, }; -enum class DataFormat : uint8_t { - UNDEFINED, - - R8_UNORM, - R8G8_UNORM, - R8G8B8_UNORM, - - R8G8B8A8_UNORM, - R8G8B8A8_UNORM_SRGB, - B8G8R8A8_UNORM, // D3D style - B8G8R8A8_UNORM_SRGB, // D3D style - - R8G8B8A8_SNORM, - R8G8B8A8_UINT, - R8G8B8A8_SINT, - - R4G4_UNORM_PACK8, - A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit - B4G4R4A4_UNORM_PACK16, - R4G4B4A4_UNORM_PACK16, - R5G6B5_UNORM_PACK16, - B5G6R5_UNORM_PACK16, - R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit - B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit - A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit. - - R16_FLOAT, - R16G16_FLOAT, - R16G16B16A16_FLOAT, - - R32_FLOAT, - R32G32_FLOAT, - R32G32B32_FLOAT, - R32G32B32A32_FLOAT, - - // Block compression formats. - // These are modern names for DXT and friends, now patent free. - // https://msdn.microsoft.com/en-us/library/bb694531.aspx - BC1_RGBA_UNORM_BLOCK, - BC1_RGBA_SRGB_BLOCK, - BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using - BC2_SRGB_BLOCK, - BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color - BC3_SRGB_BLOCK, - BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha - BC4_SNORM_BLOCK, - BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha - BC5_SNORM_BLOCK, - BC6H_UFLOAT_BLOCK, // TODO - BC6H_SFLOAT_BLOCK, - BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality. - BC7_SRGB_BLOCK, - - ETC1, - - S8, - D16, - D24_S8, - D32F, - D32F_S8, -}; - enum class ShaderStage { VERTEX, FRAGMENT, @@ -322,9 +261,12 @@ enum class NativeObject { BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, COMPATIBLE_RENDERPASS, - CURRENT_RENDERPASS, - RENDERPASS_COMMANDBUFFER, - BOUND_TEXTURE_IMAGEVIEW, + BACKBUFFER_RENDERPASS, + FRAMEBUFFER_RENDERPASS, + INIT_COMMANDBUFFER, + BOUND_TEXTURE0_IMAGEVIEW, + BOUND_TEXTURE1_IMAGEVIEW, + RENDER_MANAGER, }; enum FBColorDepth { @@ -553,6 +495,8 @@ struct DeviceCaps { bool logicOpSupported; bool framebufferCopySupported; bool framebufferBlitSupported; + bool framebufferDepthCopySupported; + bool framebufferDepthBlitSupported; }; struct TextureDesc { @@ -583,6 +527,7 @@ struct RenderPassInfo { class DrawContext { public: virtual ~DrawContext(); + bool CreatePresets(); virtual const DeviceCaps &GetDeviceCaps() const = 0; virtual uint32_t GetDataFormatSupport(DataFormat fmt) const = 0; @@ -617,6 +562,9 @@ public: virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) = 0; 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; + virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { + return false; + } // These functions should be self explanatory. // Binding a zero render target means binding the backbuffer. @@ -624,7 +572,6 @@ public: // 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 uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) = 0; @@ -662,6 +609,7 @@ public: // Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here. virtual void BeginFrame() {} virtual void EndFrame() {} + virtual void WipeQueue() {} // This should be avoided as much as possible, in favor of clearing when binding a render target, which is native // on Vulkan. @@ -674,7 +622,7 @@ public: } virtual std::string GetInfoString(InfoField info) const = 0; - virtual uintptr_t GetNativeObject(NativeObject obj) const = 0; + virtual uintptr_t GetNativeObject(NativeObject obj) = 0; virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0; @@ -689,8 +637,6 @@ public: virtual void FlushState() {} protected: - void CreatePresets(); - ShaderModule *vsPresets_[VS_MAX_PRESET]; ShaderModule *fsPresets_[FS_MAX_PRESET]; @@ -699,6 +645,10 @@ protected: }; size_t DataFormatSizeInBytes(DataFormat fmt); +bool DataFormatIsDepthStencil(DataFormat fmt); +inline bool DataFormatIsColor(DataFormat fmt) { + return !DataFormatIsDepthStencil(fmt); +} DrawContext *T3DCreateGLContext(); diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index e5d26f6bb1..72e73fe951 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -10,6 +10,9 @@ #include "math/dataconv.h" #include "util/text/utf8.h" +#include "Common/ColorConv.h" + +#include #include #include #include @@ -27,7 +30,6 @@ class D3D11DepthStencilState; class D3D11SamplerState; class D3D11RasterState; - class D3D11DrawContext : public DrawContext { public: D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd); @@ -56,12 +58,12 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; 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; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride); // These functions should be self explanatory. 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; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override; @@ -114,7 +116,7 @@ public: } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::DEVICE: return (uintptr_t)device_; @@ -194,6 +196,9 @@ private: uint8_t stencilRef_; bool stencilRefDirty_; + // Temporaries + ID3D11Texture2D *packTexture_ = nullptr; + // System info D3D_FEATURE_LEVEL featureLevel_; std::string adapterDesc_; @@ -212,6 +217,9 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.depthRangeMinusOneToOne = false; caps_.framebufferBlitSupported = false; + caps_.framebufferCopySupported = true; + caps_.framebufferDepthBlitSupported = false; + caps_.framebufferDepthCopySupported = true; D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); @@ -237,10 +245,26 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de } dxgiDevice->Release(); } - CreatePresets(); + + // Temp texture for read-back of small images. Custom textures are created on demand for larger ones. + // TODO: Should really benchmark if this extra complexity has any benefit. + D3D11_TEXTURE2D_DESC packDesc{}; + packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + packDesc.BindFlags = 0; + packDesc.Width = 512; + packDesc.Height = 512; + packDesc.ArraySize = 1; + packDesc.MipLevels = 1; + packDesc.Usage = D3D11_USAGE_STAGING; + packDesc.SampleDesc.Count = 1; + packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + hr = device_->CreateTexture2D(&packDesc, nullptr, &packTexture_); + assert(SUCCEEDED(hr)); } D3D11DrawContext::~D3D11DrawContext() { + packTexture_->Release(); + // Release references. ID3D11RenderTargetView *view = nullptr; context_->OMSetRenderTargets(1, &view, nullptr); @@ -1286,6 +1310,115 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, return false; } +bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) { + D3D11Framebuffer *fb = (D3D11Framebuffer *)src; + + if (fb) { + assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); + } + + bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT; + + ID3D11Texture2D *packTex; + if (!useGlobalPacktex) { + D3D11_TEXTURE2D_DESC packDesc{}; + packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + packDesc.BindFlags = 0; + packDesc.Width = bw; + packDesc.Height = bh; + packDesc.ArraySize = 1; + packDesc.MipLevels = 1; + packDesc.Usage = D3D11_USAGE_STAGING; + packDesc.SampleDesc.Count = 1; + D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; + switch (channelBits) { + case FB_COLOR_BIT: + packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat; + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + if (!fb) { + // Not supported. + return false; + } + packDesc.Format = fb->depthStencilFormat; + break; + default: + assert(false); + } + device_->CreateTexture2D(&packDesc, nullptr, &packTex); + } else { + switch (channelBits) { + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + if (!fb) + return false; + default: + break; + } + packTex = packTexture_; + } + + D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; + switch (channelBits) { + case FB_COLOR_BIT: + context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb ? fb->colorTex : bbRenderTargetTex_, 0, &srcBox); + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. + assert(fb); // Can't copy depth/stencil from backbuffer. Shouldn't happen thanks to checks above. + context_->CopyResource(packTex, fb->depthStencilTex); + break; + default: + assert(false); + break; + } + + // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game + // does a once-off copy, that won't work at all. + + // BIG GPU STALL + D3D11_MAPPED_SUBRESOURCE map; + HRESULT result = context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); + if (FAILED(result)) { + return false; + } + + const int srcByteOffset = by * map.RowPitch + bx * 4; + switch (channelBits) { + case FB_COLOR_BIT: + // Pixel size always 4 here because we always request BGRA8888. + ConvertFromRGBA8888((uint8_t *)pixels, (uint8_t *)map.pData + srcByteOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, format); + break; + case FB_DEPTH_BIT: + for (int y = by; y < by + bh; y++) { + float *dest = (float *)((uint8_t *)pixels + y * pixelStride * sizeof(float)); + const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); + for (int x = bx; x < bx + bw; x++) { + dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f); + } + } + break; + case FB_STENCIL_BIT: + for (int y = by; y < by + bh; y++) { + uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride; + const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); + for (int x = bx; x < bx + bw; x++) { + destStencil[x] = src[x] >> 24; + } + } + break; + } + + context_->Unmap(packTex, 0); + + if (!useGlobalPacktex) { + packTex->Release(); + } + return true; +} + void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { // TODO: deviceContext1 can actually discard. Useful on Windows Mobile. if (fbo) { @@ -1327,10 +1460,6 @@ void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, F context_->PSSetShaderResources(binding, 1, &fb->colorSRView); } -void D3D11DrawContext::BindFramebufferForRead(Framebuffer *fbo) { - // This is meaningless in D3D11 -} - uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; switch (channelBit) { @@ -1348,8 +1477,13 @@ uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int chann void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = bbWidth_; + *h = bbHeight_; + } } DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd) { diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 0a4e1938e4..3ac3058575 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -490,7 +490,6 @@ public: 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 {} uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override; @@ -530,7 +529,7 @@ public: void DrawUP(const void *vdata, int vertexCount) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::CONTEXT: return (uintptr_t)d3d_; @@ -586,7 +585,6 @@ private: D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx) : d3d_(d3d), d3dEx_(d3dEx), adapterId_(adapterId), device_(device), deviceEx_(deviceEx), caps_{} { - CreatePresets(); if (FAILED(d3d->GetAdapterIdentifier(adapterId, 0, &identifier_))) { ELOG("Failed to get adapter identifier: %d", adapterId); } @@ -603,6 +601,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.tesselationShaderSupported = false; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = false; + caps_.framebufferDepthBlitSupported = true; + caps_.framebufferDepthCopySupported = false; if (d3d) { D3DDISPLAYMODE displayMode; d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode); @@ -634,6 +634,10 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { } D3D9Pipeline *pipeline = new D3D9Pipeline(device_); for (auto iter : desc.shaders) { + if (!iter) { + ELOG("NULL shader passed to CreateGraphicsPipeline"); + return false; + } if (iter->GetStage() == ShaderStage::FRAGMENT) { pipeline->pshader = static_cast(iter); } @@ -948,20 +952,24 @@ void D3D9Context::SetBlendFactor(float color[4]) { } bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, size_t size) { - LPD3DXMACRO defines = NULL; - LPD3DXINCLUDE includes = NULL; + LPD3DXMACRO defines = nullptr; + LPD3DXINCLUDE includes = nullptr; DWORD flags = 0; - LPD3DXBUFFER codeBuffer; - LPD3DXBUFFER errorBuffer; + LPD3DXBUFFER codeBuffer = nullptr; + LPD3DXBUFFER errorBuffer = nullptr; const char *source = (const char *)data; const char *profile = stage_ == ShaderStage::FRAGMENT ? "ps_2_0" : "vs_2_0"; HRESULT hr = dyn_D3DXCompileShader(source, (UINT)strlen(source), defines, includes, "main", profile, flags, &codeBuffer, &errorBuffer, nullptr); if (FAILED(hr)) { - const char *error = (const char *)errorBuffer->GetBufferPointer(); + const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)"; + if (hr == ERROR_MOD_NOT_FOUND) { + // No D3D9-compatible shader compiler installed. + error = "D3D9 shader compiler not installed"; + } OutputDebugStringA(source); OutputDebugStringA(error); - errorBuffer->Release(); - + if (errorBuffer) + errorBuffer->Release(); if (codeBuffer) codeBuffer->Release(); return false; @@ -1119,8 +1127,13 @@ void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChan void D3D9Context::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = targetWidth_; + *h = targetHeight_; + } } bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 12c6b0d6b2..36532fabe9 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "base/logging.h" #include "math/dataconv.h" @@ -18,6 +19,8 @@ extern void bindDefaultFBO(); #endif +// #define DEBUG_READ_PIXELS 1 + // Workaround for Retroarch. Simply declare // extern GLuint g_defaultFBO; // and set is as appropriate. Can adjust the variables in ext/native/base/display.h as @@ -465,12 +468,12 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; 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; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) override; // These functions should be self explanatory. 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; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override; @@ -557,7 +560,7 @@ public: } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { return 0; } @@ -588,8 +591,6 @@ private: }; OpenGLContext::OpenGLContext() { - CreatePresets(); - // TODO: Detect more caps if (gl_extensions.IsGLES) { if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) { @@ -601,6 +602,7 @@ OpenGLContext::OpenGLContext() { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; } caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object; + caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported; } OpenGLContext::~OpenGLContext() { @@ -729,11 +731,121 @@ void OpenGLTexture::AutoGenMipmaps() { } } -void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { - int internalFormat; - int format; - int type; +class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder { +public: + OpenGLFramebuffer() { + register_gl_resource_holder(this, "framebuffer", 0); + } + ~OpenGLFramebuffer(); + void GLLost() override { + handle = 0; + color_texture = 0; + z_stencil_buffer = 0; + z_buffer = 0; + stencil_buffer = 0; + } + + void GLRestore() override { + ELOG("Restoring framebuffers not yet implemented"); + } + + GLuint handle = 0; + GLuint color_texture = 0; + GLuint z_stencil_buffer = 0; // Either this is set, or the two below. + GLuint z_buffer = 0; + GLuint stencil_buffer = 0; + + int width; + int height; + FBColorDepth colorDepth; +}; + + +// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage. +static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) { + alignment = 4; + switch (fmt) { + case DataFormat::R8G8B8A8_UNORM: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + + case DataFormat::D32F: + internalFormat = GL_DEPTH_COMPONENT; + format = GL_DEPTH_COMPONENT; + type = GL_FLOAT; + break; + +#ifndef USING_GLES2 + case DataFormat::S8: + internalFormat = GL_STENCIL_INDEX; + format = GL_STENCIL_INDEX; + type = GL_UNSIGNED_BYTE; + alignment = 1; + break; +#endif + + case DataFormat::R8G8B8_UNORM: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_BYTE; + alignment = 1; + break; + + case DataFormat::B4G4R4A4_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + alignment = 2; + break; + + case DataFormat::B5G6R5_UNORM_PACK16: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + alignment = 2; + break; + + case DataFormat::B5G5R5A1_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_5_5_5_1; + alignment = 2; + break; + +#ifndef USING_GLES2 + case DataFormat::A4R4G4B4_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + alignment = 2; + break; + + case DataFormat::R5G6B5_UNORM_PACK16: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5_REV; + alignment = 2; + break; + + case DataFormat::A1R5G5B5_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + alignment = 2; + break; +#endif + + default: + ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt); + return false; + } + return true; +} + +void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { if (width != width_ || height != height_ || depth != depth_) { // When switching to texStorage we need to handle this correctly. width_ = width; @@ -741,26 +853,11 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth_ = depth; } - switch (format_) { - case DataFormat::R8G8B8A8_UNORM: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case DataFormat::B4G4R4A4_UNORM_PACK16: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4; - break; -#ifndef USING_GLES2 - case DataFormat::A4R4G4B4_UNORM_PACK16: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4_REV; - break; -#endif - default: - ELOG("Thin3d GL: Unsupported texture format %d", (int)format_); + GLuint internalFormat; + GLuint format; + GLuint type; + int alignment; + if (!Thin3DFormatToFormatAndType(format_, internalFormat, format, type, alignment)) { return; } @@ -776,6 +873,80 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int CHECK_GL_ERROR_IF_DEBUG(); } + +#ifdef DEBUG_READ_PIXELS +// TODO: Make more generic. +static void LogReadPixelsError(GLenum error) { + switch (error) { + case GL_NO_ERROR: + break; + case GL_INVALID_ENUM: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); + break; +#ifndef USING_GLES2 + case GL_STACK_UNDERFLOW: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); + break; + case GL_STACK_OVERFLOW: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); + break; +#endif + default: + ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); + break; + } +} +#endif + +bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; + fbo_bind_fb_target(true, fb ? fb->handle : 0); + + // Reads from the "bound for read" framebuffer. + if (gl_extensions.GLES3 || !gl_extensions.IsGLES) + glReadBuffer(GL_COLOR_ATTACHMENT0); + + CHECK_GL_ERROR_IF_DEBUG(); + + GLuint internalFormat; + GLuint format; + GLuint type; + int alignment; + if (!Thin3DFormatToFormatAndType(dataFormat, internalFormat, format, type, alignment)) { + assert(false); + } + // Apply the correct alignment. + glPixelStorei(GL_PACK_ALIGNMENT, alignment); + if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { + // Some drivers seem to require we specify this. See #8254. + glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride); + } + + glReadPixels(x, y, w, h, format, type, pixels); +#ifdef DEBUG_READ_PIXELS + LogReadPixelsError(glGetError()); +#endif + + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + } + CHECK_GL_ERROR_IF_DEBUG(); + return true; +} + + Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { return new OpenGLTexture(desc); } @@ -1243,36 +1414,6 @@ void OpenGLInputLayout::Unapply() { } } -class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder { -public: - OpenGLFramebuffer() { - register_gl_resource_holder(this, "framebuffer", 0); - } - ~OpenGLFramebuffer(); - - void GLLost() override { - handle = 0; - color_texture = 0; - z_stencil_buffer = 0; - z_buffer = 0; - stencil_buffer = 0; - } - - void GLRestore() override { - ELOG("Restoring framebuffers not yet implemented"); - } - - GLuint handle = 0; - GLuint color_texture = 0; - GLuint z_stencil_buffer = 0; // Either this is set, or the two below. - GLuint z_buffer = 0; - GLuint stencil_buffer = 0; - - int width; - int height; - FBColorDepth colorDepth; -}; - // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. @@ -1587,13 +1728,6 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render CHECK_GL_ERROR_IF_DEBUG(); } -// For GL_EXT_FRAMEBUFFER_BLIT and similar. -void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - fbo_bind_fb_target(true, fb->handle); - CHECK_GL_ERROR_IF_DEBUG(); -} - void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc; OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst; @@ -1731,8 +1865,13 @@ OpenGLFramebuffer::~OpenGLFramebuffer() { void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = targetWidth_; + *h = targetHeight_; + } } uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b715d5ea23..4e6eff5ddd 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -30,6 +30,7 @@ #include "math/lin/matrix4x4.h" #include "math/dataconv.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" @@ -134,13 +135,6 @@ static const VkStencilOp stencilOpToVK[8] = { VK_STENCIL_OP_DECREMENT_AND_WRAP, }; -static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) { - f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f); - f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f); - f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f); - f[3] = ((u >> 24) & 0xFF) * (1.0f / 255.0f); -} - class VKBlendState : public BlendState { public: VkPipelineColorBlendStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; @@ -317,9 +311,9 @@ struct DescriptorSetKey { class VKTexture : public Texture { public: - VKTexture(VulkanContext *vulkan, const TextureDesc &desc) + VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc) : vulkan_(vulkan), format_(desc.format), mipLevels_(desc.mipLevels) { - Create(desc); + Create(cmd, desc); } ~VKTexture() { @@ -329,9 +323,9 @@ public: VkImageView GetImageView() { return vkTex_->GetImageView(); } private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); + void SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); - bool Create(const TextureDesc &desc); + bool Create(VkCommandBuffer cmd, const TextureDesc &desc); void Destroy() { if (vkTex_) { @@ -349,14 +343,7 @@ private: DataFormat format_; }; -// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine -// to let them have individual non-pooled allocations. Until it's not fine. We'll see. -struct VKImage { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - VkImageLayout layout; -}; +class VKFramebuffer; class VKContext : public DrawContext { public: @@ -387,12 +374,12 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; 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; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride); // These functions should be self explanatory. 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; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override; @@ -431,9 +418,9 @@ public: void BeginFrame() override; void EndFrame() override; + void WipeQueue() override; void FlushState() override { - ApplyDynamicState(); } void WaitRenderCompletion(Framebuffer *fbo) override; @@ -459,21 +446,25 @@ public: std::vector GetFeatureList() const override; std::vector GetExtensionList() const override; - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { - case NativeObject::COMPATIBLE_RENDERPASS: + case NativeObject::FRAMEBUFFER_RENDERPASS: // Return a representative renderpass. - if (curRenderPass_ == vulkan_->GetSurfaceRenderPass()) - return (uintptr_t)curRenderPass_; - else - return (uintptr_t)renderPasses_[0]; - case NativeObject::CURRENT_RENDERPASS: - return (uintptr_t)curRenderPass_; - case NativeObject::RENDERPASS_COMMANDBUFFER: - return (uintptr_t)cmd_; - case NativeObject::BOUND_TEXTURE_IMAGEVIEW: + return (uintptr_t)renderManager_.GetRenderPass(0); + case NativeObject::BACKBUFFER_RENDERPASS: + return (uintptr_t)renderManager_.GetBackbufferRenderPass(); + case NativeObject::COMPATIBLE_RENDERPASS: + return (uintptr_t)renderManager_.GetCompatibleRenderPass(); + case NativeObject::INIT_COMMANDBUFFER: + return (uintptr_t)renderManager_.GetInitCmd(); + case NativeObject::BOUND_TEXTURE0_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; + case NativeObject::BOUND_TEXTURE1_IMAGEVIEW: + return (uintptr_t)boundImageView_[1]; + case NativeObject::RENDER_MANAGER: + return (uintptr_t)&renderManager_; default: + Crash(); return 0; } } @@ -481,62 +472,35 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; private: - void ApplyDynamicState(); - void DirtyDynamicState(); - - void EndCurrentRenderpass(); - VkCommandBuffer AllocCmdBuf(); - - static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - VulkanContext *vulkan_ = nullptr; + VulkanRenderManager renderManager_; + VKPipeline *curPipeline_ = nullptr; VKBuffer *curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; VKBuffer *curIBuffer_ = nullptr; int curIBufferOffset_ = 0; - VkDescriptorSetLayout descriptorSetLayout_; - VkPipelineLayout pipelineLayout_; - VkPipelineCache pipelineCache_; - - inline int RPIndex(RPAction color, RPAction depth) { - return (int)depth * 3 + (int)color; - } - - // Renderpasses, all combination of preserving or clearing or dont-care-ing fb contents. - VkRenderPass renderPasses_[9]; + VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; + VKFramebuffer *curFramebuffer_ = nullptr; VkDevice device_; VkQueue queue_; int queueFamilyIndex_; - // State to apply at the next draw call if viewportDirty or scissorDirty are true. - bool viewportDirty_; - VkViewport viewport_{}; - bool scissorDirty_; - VkRect2D scissor_; - - int curWidth_ = -1; - int curHeight_ = -1; - enum { - MAX_BOUND_TEXTURES = 1, + MAX_BOUND_TEXTURES = 2, MAX_FRAME_COMMAND_BUFFERS = 256, }; - VKTexture *boundTextures_[MAX_BOUND_TEXTURES]; - VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]; - VkImageView boundImageView_[MAX_BOUND_TEXTURES]; + VKTexture *boundTextures_[MAX_BOUND_TEXTURES]{}; + VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]{}; + VkImageView boundImageView_[MAX_BOUND_TEXTURES]{}; struct FrameData { VulkanPushBuffer *pushBuffer; - VkCommandPool cmdPool_; - VkCommandBuffer cmdBufs[MAX_FRAME_COMMAND_BUFFERS]; - int startCmdBufs_; - int numCmdBufs; - // Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to // worry about invalidating descriptors pointing to deleted textures. // However! ARM is not a fan of doing it this way. @@ -546,14 +510,9 @@ private: FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; - int frameNum_ = 0; VulkanPushBuffer *push_ = nullptr; DeviceCaps caps_{}; - - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; - VkRenderPass curRenderPass_ = VK_NULL_HANDLE; - VkCommandBuffer cmd_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -677,7 +636,7 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(const TextureDesc &desc) { +bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { // Zero-sized textures not allowed. if (desc.width * desc.height * desc.depth == 0) return false; @@ -689,14 +648,14 @@ bool VKTexture::Create(const TextureDesc &desc) { vkTex_ = new VulkanTexture(vulkan_); if (desc.initData.size()) { for (int i = 0; i < (int)desc.initData.size(); i++) { - this->SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); + this->SetImageData(cmd, 0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); } } return true; } VKContext::VKContext(VulkanContext *vulkan) - : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{} { + : vulkan_(vulkan), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -704,23 +663,15 @@ VKContext::VKContext(VulkanContext *vulkan) caps_.dualSourceBlend = vulkan->GetFeaturesAvailable().dualSrcBlend != 0; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = true; - caps_.preferredDepthBufferFormat = DataFormat::D24_S8; + caps_.framebufferDepthBlitSupported = false; // Can be checked for. + caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. + caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. + device_ = vulkan->GetDevice(); queue_ = vulkan->GetGraphicsQueue(); queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex(); - scissor_.offset.x = 0; - scissor_.offset.y = 0; - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - viewport_.x = 0; - viewport_.y = 0; - viewport_.width = pixel_xres; - viewport_.height = pixel_yres; - viewport_.minDepth = 0.0f; - viewport_.maxDepth = 0.0f; memset(boundTextures_, 0, sizeof(boundTextures_)); - CreatePresets(); VkDescriptorPoolSize dpTypes[2]; dpTypes[0].descriptorCount = 200; @@ -739,11 +690,9 @@ VKContext::VKContext(VulkanContext *vulkan) p.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex(); for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); - assert(VK_SUCCESS == res); - res = vkCreateCommandPool(device_, &p, nullptr, &frame_[i].cmdPool_); - assert(VK_SUCCESS == res); frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); + VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); + assert(res == VK_SUCCESS); } // binding 0 - uniform data @@ -775,125 +724,25 @@ VKContext::VKContext(VulkanContext *vulkan) assert(VK_SUCCESS == res); pipelineCache_ = vulkan_->CreatePipelineCache(); - - // Create a bunch of render pass objects, for normal rendering with a depth buffer, - // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. - VkAttachmentDescription attachments[2] = {}; - attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; - 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; - - for (int depth = 0; depth < 3; depth++) { - switch ((RPAction)depth) { - case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - for (int color = 0; color < 3; color++) { - switch ((RPAction)color) { - case RPAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RPAction)color, (RPAction)depth)]); - } - } } VKContext::~VKContext() { - for (int i = 0; i < 9; i++) { - vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); - } // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descriptorPool); frame_[i].pushBuffer->Destroy(vulkan_); delete frame_[i].pushBuffer; - vulkan_->Delete().QueueDeleteCommandPool(frame_[i].cmdPool_); } vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); } -// Effectively wiped every frame, just allocate new ones! -VkCommandBuffer VKContext::AllocCmdBuf() { - FrameData *frame = &frame_[frameNum_]; - - if (frame->numCmdBufs >= MAX_FRAME_COMMAND_BUFFERS) - Crash(); - - if (frame->cmdBufs[frame->numCmdBufs]) { - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; - } - - VkCommandBufferAllocateInfo alloc{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - alloc.commandBufferCount = 1; - alloc.commandPool = frame->cmdPool_; - alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - VkResult result = vkAllocateCommandBuffers(vulkan_->GetDevice(), &alloc, &frame->cmdBufs[frame->numCmdBufs]); - assert(result == VK_SUCCESS); - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; -} - void VKContext::BeginFrame() { - vulkan_->BeginFrame(); + renderManager_.BeginFrame(); - FrameData &frame = frame_[frameNum_]; - frame.startCmdBufs_ = 0; - frame.numCmdBufs = 0; - vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0); + FrameData &frame = frame_[vulkan_->GetCurFrame()]; push_ = frame.pushBuffer; // OK, we now know that nothing is reading from this frame's data pushbuffer, @@ -903,11 +752,6 @@ void VKContext::BeginFrame() { frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); assert(result == VK_SUCCESS); - - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - scissorDirty_ = true; - viewportDirty_ = true; } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -915,35 +759,22 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { } void VKContext::EndFrame() { - EndCurrentRenderpass(); - - if (cmd_) - Crash(); - - // Cap off and submit all the command buffers we recorded during the frame. - FrameData &frame = frame_[frameNum_]; - for (int i = frame.startCmdBufs_; i < frame.numCmdBufs; i++) { - vkEndCommandBuffer(frame.cmdBufs[i]); - vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]); - } - frame.startCmdBufs_ = frame.numCmdBufs; - // Stop collecting data in the frame's data pushbuffer. push_->End(); - vulkan_->EndFrame(); - frameNum_++; - if (frameNum_ >= vulkan_->GetInflightFrames()) - frameNum_ = 0; + renderManager_.Finish(); + push_ = nullptr; +} - DirtyDynamicState(); +void VKContext::WipeQueue() { + renderManager_.Wipe(); } VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { DescriptorSetKey key; - FrameData *frame = &frame_[frameNum_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; key.texture_ = boundTextures_[0]; key.sampler_ = boundSamplers_[0]; @@ -1014,6 +845,10 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { int i = 0; for (auto &iter : desc.shaders) { VKShaderModule *vkshader = (VKShaderModule *)iter; + if (!vkshader) { + ELOG("CreateGraphicsPipeline got passed a null shader"); + return nullptr; + } VkPipelineShaderStageCreateInfo &stage = stages[i++]; stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage.pNext = nullptr; @@ -1064,7 +899,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = vulkan_->GetSurfaceRenderPass(); + info.renderPass = renderManager_.GetBackbufferRenderPass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); @@ -1081,43 +916,25 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } void VKContext::SetScissorRect(int left, int top, int width, int height) { - scissor_.offset.x = left; - scissor_.offset.y = top; - scissor_.extent.width = width; - scissor_.extent.height = height; - scissorDirty_ = true; + VkRect2D scissor{ {left, top}, {(uint32_t)width, (uint32_t)height} }; + renderManager_.SetScissor(scissor); } void VKContext::SetViewports(int count, Viewport *viewports) { if (count > 0) { - viewport_.x = viewports[0].TopLeftX; - viewport_.y = viewports[0].TopLeftY; - viewport_.width = viewports[0].Width; - viewport_.height = viewports[0].Height; - viewport_.minDepth = viewports[0].MinDepth; - viewport_.maxDepth = viewports[0].MaxDepth; - viewportDirty_ = true; + VkViewport viewport; + viewport.x = viewports[0].TopLeftX; + viewport.y = viewports[0].TopLeftY; + viewport.width = viewports[0].Width; + viewport.height = viewports[0].Height; + viewport.minDepth = viewports[0].MinDepth; + viewport.maxDepth = viewports[0].MaxDepth; + renderManager_.SetViewport(viewport); } } void VKContext::SetBlendFactor(float color[4]) { - vkCmdSetBlendConstants(cmd_, color); -} - -void VKContext::ApplyDynamicState() { - if (scissorDirty_) { - vkCmdSetScissor(cmd_, 0, 1, &scissor_); - scissorDirty_ = false; - } - if (viewportDirty_) { - vkCmdSetViewport(cmd_, 0, 1, &viewport_); - viewportDirty_ = false; - } -} - -void VKContext::DirtyDynamicState() { - scissorDirty_ = true; - viewportDirty_ = true; + renderManager_.SetBlendFactor(color); } InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { @@ -1145,10 +962,10 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { } Texture *VKContext::CreateTexture(const TextureDesc &desc) { - return new VKTexture(vulkan_, desc); + return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc); } -void VKTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { +void VKTexture::SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { VkFormat vulkanFormat = DataFormatToVulkan(format_); if (stride == 0) { stride = width * (int)DataFormatSizeInBytes(format_); @@ -1161,7 +978,7 @@ void VKTexture::SetImageData(int x, int y, int z, int width, int height, int dep for (int y = 0; y < height; y++) { memcpy(dstData + rowPitch * y, data + stride * y, width * bytesPerPixel); } - vkTex_->Unlock(); + vkTex_->Unlock(cmd); } inline void CopySide(VkStencilOpState &dest, const StencilSide &src) { @@ -1286,8 +1103,6 @@ void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void VKContext::Draw(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *vbuf = curVBuffers_[0]; VkBuffer vulkanVbuf; @@ -1295,18 +1110,14 @@ void VKContext::Draw(int vertexCount, int offset) { uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - vkCmdDraw(cmd_, vertexCount, 1, offset, 0); + + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } void VKContext::DrawIndexed(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *ibuf = static_cast(curIBuffer_); VKBuffer *vbuf = static_cast(curVBuffers_[0]); @@ -1315,72 +1126,35 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), &vulkanIbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - - vkCmdBindIndexBuffer(cmd_, vulkanIbuf, ibBindOffset, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(cmd_, vertexCount, 1, 0, offset, 0); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { - ApplyDynamicState(); - VkBuffer vulkanVbuf, vulkanUBObuf; size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); - - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - vkCmdDraw(cmd_, vertexCount, 1, 0, 0); + + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } -// TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. -void VKContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { - if (!curRenderPass_) { - ELOG("Clear: Need an active render pass"); - return; - } +void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { + int mask = 0; + if (clearMask & FBChannel::FB_COLOR_BIT) + mask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (clearMask & FBChannel::FB_DEPTH_BIT) + mask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (clearMask & FBChannel::FB_STENCIL_BIT) + mask |= VK_IMAGE_ASPECT_STENCIL_BIT; - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (mask & FBChannel::FB_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(colorval, attachment.clearValue.color.float32); - } - if (mask & (FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (mask & FBChannel::FB_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = depthVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (mask & FBChannel::FB_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = stencilVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - if (numAttachments) { - vkCmdClearAttachments(cmd_, numAttachments, attachments, 1, &rc); - } + renderManager_.Clear(colorval, depthVal, stencilVal, mask); } DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) { @@ -1468,576 +1242,131 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { } } -void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { - VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - ici.arrayLayers = 1; - ici.mipLevels = 1; - ici.extent.width = width; - ici.extent.height = height; - ici.extent.depth = 1; - ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - ici.imageType = VK_IMAGE_TYPE_2D; - ici.samples = VK_SAMPLE_COUNT_1_BIT; - ici.tiling = VK_IMAGE_TILING_OPTIMAL; - ici.format = format; - ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - if (color) { - ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } else { - ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); - - // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers - - VkMemoryRequirements memreq; - vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); - - VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - alloc.allocationSize = memreq.size; - vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); - VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); - assert(res == VK_SUCCESS); - res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); - assert(res == VK_SUCCESS); - - VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - ivci.format = ici.format; - ivci.image = img.image; - ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; - ivci.subresourceRange.aspectMask = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - ivci.subresourceRange.layerCount = 1; - ivci.subresourceRange.levelCount = 1; - res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view); - assert(res == VK_SUCCESS); - - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (initialLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - } - barrier.newLayout = initialLayout; - barrier.subresourceRange.aspectMask = ivci.subresourceRange.aspectMask; - vkCmdPipelineBarrier(vulkan->GetInitCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - img.layout = VK_IMAGE_LAYOUT_UNDEFINED; -} - // A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns. // It also has a reference to the command buffer that it was last rendered to with. // If it needs to be transitioned, and the frame number matches, use it, otherwise // use this frame's init command buffer. class VKFramebuffer : public Framebuffer { public: - VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {} + VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) {} ~VKFramebuffer() { - vulkan_->Delete().QueueDeleteImage(color.image); - vulkan_->Delete().QueueDeleteImage(depth.image); - vulkan_->Delete().QueueDeleteImageView(color.view); - vulkan_->Delete().QueueDeleteImageView(depth.view); - vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); - vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + delete buf_; } - VkFramebuffer framebuf = VK_NULL_HANDLE; - VKImage color{}; - VKImage depth{}; - int width = 0; - int height = 0; - - // These belong together, see above. - VkCommandBuffer cmdBuf = VK_NULL_HANDLE; - int frameCount = 0; - + VKRFramebuffer *GetFB() const { return buf_; } private: - VulkanContext *vulkan_; + VKRFramebuffer *buf_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { - VKFramebuffer *fb = new VKFramebuffer(vulkan_); - fb->width = desc.width; - fb->height = desc.height; - - CreateImage(vulkan_, fb->color, fb->width, fb->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); - CreateImage(vulkan_, fb->depth, fb->width, fb->height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); - - VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - VkImageView views[2]{}; - - fbci.renderPass = renderPasses_[0]; - fbci.attachmentCount = 2; - fbci.pAttachments = views; - views[0] = fb->color.view; - views[1] = fb->depth.view; - fbci.width = fb->width; - fbci.height = fb->height; - fbci.layers = 1; - - vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &fb->framebuf); + VkCommandBuffer cmd = renderManager_.GetInitCmd(); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetRenderPass(0), desc.width, desc.height); + VKFramebuffer *fb = new VKFramebuffer(vkrfb); return fb; } void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { - // Can't copy during render passes. - EndCurrentRenderpass(); - VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - VkImageCopy copy{}; - copy.srcOffset.x = x; - copy.srcOffset.y = y; - copy.srcOffset.z = z; - copy.srcSubresource.mipLevel = level; - copy.srcSubresource.layerCount = 1; - copy.dstOffset.x = dstX; - copy.dstOffset.y = dstY; - copy.dstOffset.z = dstZ; - copy.dstSubresource.mipLevel = dstLevel; - copy.dstSubresource.layerCount = 1; - copy.extent.width = width; - copy.extent.height = height; - copy.extent.depth = depth; + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - // We're gonna tack copies onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - copy.srcSubresource.aspectMask = 0; - copy.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); - } + renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }, aspectMask); } bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - // We're gonna tack blits onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - VkImageBlit blit{}; - blit.srcOffsets[0].x = srcX1; - blit.srcOffsets[0].y = srcY1; - blit.srcOffsets[0].z = 0; - blit.srcOffsets[1].x = srcX2; - blit.srcOffsets[1].y = srcY2; - blit.srcOffsets[1].z = 1; - blit.srcSubresource.mipLevel = 0; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0].x = dstX1; - blit.dstOffsets[0].y = dstY1; - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = dstX2; - blit.dstOffsets[1].y = dstY2; - blit.dstOffsets[1].z = 1; - blit.dstSubresource.mipLevel = 0; - blit.dstSubresource.layerCount = 1; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - blit.srcSubresource.aspectMask = 0; - blit.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } + renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, aspectMask, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); return true; } -void VKContext::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - img.layout = barrier.newLayout; -} +bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { + VKFramebuffer *src = (VKFramebuffer *)srcfb; -void VKContext::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - img.layout = barrier.newLayout; -} + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; -void VKContext::EndCurrentRenderpass() { - if (curRenderPass_ != VK_NULL_HANDLE) { - vkCmdEndRenderPass(cmd_); - curRenderPass_ = VK_NULL_HANDLE; - curFramebuffer_ = VK_NULL_HANDLE; - cmd_ = VK_NULL_HANDLE; - } + renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride); + return true; } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { - VkFramebuffer framebuf; - VkCommandBuffer cmdBuf; - int w; - int h; - VkImageLayout prevLayout; - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - framebuf = fb->framebuf; - w = fb->width; - h = fb->height; - prevLayout = fb->color.layout; - cmdBuf = fb->cmdBuf; - } else { - framebuf = vulkan_->GetSurfaceFramebuffer(); - w = vulkan_->GetWidth(); - h = vulkan_->GetHeight(); - cmdBuf = vulkan_->GetSurfaceCommandBuffer(); - } + VKFramebuffer *fb = (VKFramebuffer *)fbo; + VKRRenderPassAction color = (VKRRenderPassAction)rp.color; + VKRRenderPassAction depth = (VKRRenderPassAction)rp.depth; - if (framebuf == curFramebuffer_) { - if (framebuf == 0) - Crash(); - if (!curRenderPass_) - Crash(); - // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. - // We will try to avoid this as much as possible. - VkClearAttachment clear[2]{}; - int count = 0; - if (rp.color == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - Uint8x4ToFloat4(rp.clearColor, clear[count].clearValue.color.float32); - clear[count].colorAttachment = 0; - count++; - } - if (rp.depth == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - clear[count].clearValue.depthStencil.depth = rp.clearDepth; - clear[count].clearValue.depthStencil.stencil = rp.clearStencil; - clear[count].colorAttachment = 0; - count++; - } - if (count > 0) { - VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; - vkCmdClearAttachments(cmdBuf, count, clear, 1, &rc); - } - // We're done. - return; - } - - // OK, we're switching framebuffers. - EndCurrentRenderpass(); - VkRenderPass renderPass; - int numClearVals = 0; - VkClearValue clearVal[2]; - memset(clearVal, 0, sizeof(clearVal)); - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - fb->cmdBuf = AllocCmdBuf(); - if (!fb->cmdBuf) - Crash(); - fb->frameCount = frameNum_; - cmd_ = fb->cmdBuf; - VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - VkResult res = vkBeginCommandBuffer(cmd_, &begin); - assert(res == VK_SUCCESS); - // Now, if the image needs transitioning, let's transition. - // The backbuffer does not, that's handled by VulkanContext. - if (fb->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - break; - } - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; - } - if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->depth.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->depth.image; - barrier.srcAccessMask = 0; - switch (fb->depth.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - } - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->depth.layout = barrier.newLayout; - } - - renderPass = renderPasses_[RPIndex(rp.color, rp.depth)]; - // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (rp.color == RPAction::CLEAR) { - Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32); - numClearVals = 1; - } - if (rp.depth == RPAction::CLEAR) { - clearVal[1].depthStencil.depth = rp.clearDepth; - clearVal[1].depthStencil.stencil = rp.clearStencil; - numClearVals = 2; - } - } else { - cmd_ = vulkan_->GetSurfaceCommandBuffer(); - renderPass = vulkan_->GetSurfaceRenderPass(); - numClearVals = 2; - } - - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = renderPass; - rp_begin.framebuffer = framebuf; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = w; - rp_begin.renderArea.extent.height = h; - rp_begin.clearValueCount = numClearVals; - rp_begin.pClearValues = numClearVals ? clearVal : nullptr; - vkCmdBeginRenderPass(cmd_, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curFramebuffer_ = framebuf; - curRenderPass_ = renderPass; - curWidth_ = w; - curHeight_ = h; + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); + curFramebuffer_ = fb; } // color must be 0, for now. void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - boundImageView_[0] = fb->color.view; - // If we already have the right layout, nothing else to do. - if (fb->color.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - return; - VkCommandBuffer transitionCmdBuf; - if (fb->cmdBuf && fb->frameCount == frameNum_) { - // If the framebuffer has a "live" command buffer, we can directly use it to transition it for sampling. - transitionCmdBuf = fb->cmdBuf; - } else { - // If not, we can just do it at the "start" of the frame. - transitionCmdBuf = vulkan_->GetInitCommandBuffer(); + if (fb == curFramebuffer_) { + Crash(); } - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; - switch (barrier.oldLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - } - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + int aspect = 0; + if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; - // we're between passes so it's OK. - // ARM Best Practices guide recommends these stage bits. - vkCmdPipelineBarrier(transitionCmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; + boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } -void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ } - uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { - // TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last. + if (!fbo) + return 0; + VKFramebuffer *fb = (VKFramebuffer *)fbo; - return (uintptr_t)fb->color.image; + VkImageView view = VK_NULL_HANDLE; + switch (channelBit) { + case FB_COLOR_BIT: + view = fb->GetFB()->color.imageView; + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + view = fb->GetFB()->depth.imageView; + break; + } + return (uintptr_t)view; } void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->GetFB()->width; + *h = fb->GetFB()->height; + } else { + *w = vulkan_->GetBackbufferWidth(); + *h = vulkan_->GetBackbufferHeight(); + } } void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { - // Noop + switch (ev) { + case Event::LOST_BACKBUFFER: + renderManager_.DestroyBackbuffers(); + break; + case Event::GOT_BACKBUFFER: + renderManager_.CreateBackbuffers(); + break; + default: + assert(false); + break; + } } } // namespace Draw diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 1f99d2b150..65f1aab6fe 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -783,7 +783,7 @@ void TextEdit::Draw(UIContext &dc) { float w, h; Bounds textBounds = bounds_; - textBounds.x = textX; + textBounds.x = textX - scrollPos_; if (text_.empty()) { if (placeholderText_.size()) { @@ -797,13 +797,14 @@ void TextEdit::Draw(UIContext &dc) { if (HasFocus()) { // Hack to find the caret position. Might want to find a better way... dc.MeasureTextCount(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), caret_, &w, &h, ALIGN_VCENTER | ALIGN_LEFT); - float caretX = w; - caretX += textX; - + float caretX = w - scrollPos_; if (caretX > bounds_.w) { - // Scroll text to the left if the caret won't fit. Not ideal but looks better than not scrolling it. - textX -= caretX - bounds_.w; + scrollPos_ += caretX - bounds_.w; } + if (caretX < 0) { + scrollPos_ += caretX; + } + caretX += textX; dc.FillRect(UI::Drawable(textColor), Bounds(caretX - 1, bounds_.y + 2, 3, bounds_.h - 4)); } dc.PopScissor(); diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index aada63d9ff..93afab7c18 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -780,7 +780,7 @@ private: class TextEdit : public View { public: TextEdit(const std::string &text, const std::string &placeholderText, LayoutParams *layoutParams = 0); - void SetText(const std::string &text) { text_ = text; caret_ = (int)text_.size(); } + void SetText(const std::string &text) { text_ = text; scrollPos_ = 0; caret_ = (int)text_.size(); } void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; } const std::string &GetText() const { return text_; } void SetMaxLen(size_t maxLen) { maxLen_ = maxLen; } @@ -802,6 +802,7 @@ private: uint32_t textColor_; bool hasTextColor_ = false; int caret_; + int scrollPos_ = 0; size_t maxLen_; bool ctrlDown_ = false; // TODO: Make some global mechanism for this. // TODO: Selections diff --git a/ext/native/util/hash/hash.cpp b/ext/native/util/hash/hash.cpp index 3ebaea7d02..3bb840f84d 100644 --- a/ext/native/util/hash/hash.cpp +++ b/ext/native/util/hash/hash.cpp @@ -3,35 +3,7 @@ namespace hash { -// uint32_t -// WARNING - may read one more byte! Fine if the input is a null-terminated string. -// Implementation from Wikipedia. -uint32_t Fletcher(const uint8_t *data_uint8, size_t length) { - const uint16_t *data = (const uint16_t *)data_uint8; - size_t len = (length + 1) / 2; - uint32_t sum1 = 0xffff, sum2 = 0xffff; - - while (len) { - size_t tlen = len > 360 ? 360 : len; - len -= tlen; - - do { - sum1 += *data++; - sum2 += sum1; - } while (--tlen); - - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - } - - /* Second reduction step to reduce sums to 16 bits */ - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - return sum2 << 16 | sum1; -} - // Implementation from Wikipedia -// Slightly slower than Fletcher above, but slighly more reliable. #define MOD_ADLER 65521 // data: Pointer to the data to be summed; len is in bytes uint32_t Adler32(const uint8_t *data, size_t len) { diff --git a/ext/native/util/hash/hash.h b/ext/native/util/hash/hash.h index 0c7ac64326..2f7e4305b6 100644 --- a/ext/native/util/hash/hash.h +++ b/ext/native/util/hash/hash.h @@ -1,14 +1,14 @@ #ifndef _UTIL_HASH_HASH_H #define _UTIL_HASH_HASH_H -#include +#include #include "base/basictypes.h" namespace hash { -uint32_t Fletcher(const uint8_t *data_u8, size_t length); // FAST. Length & 1 == 0. -uint32_t Adler32(const uint8_t *data, size_t len); // Fairly accurate, slightly slower +// Fairly decent function for hashing strings. +uint32_t Adler32(const uint8_t *data, size_t len); } // namespace hash diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 063599a2d7..1f02b5e381 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Mar 13 14:55:51 CET 2017 +#Thu Oct 26 10:42:05 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index 8e0e1ab082..90c05dbc74 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "gfx/gl_lost_manager.h" #include "headless/SDLHeadlessHost.h" @@ -54,6 +55,8 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); + assert(success); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 1cb6e5230f..4c81509ec5 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -8,6 +8,7 @@ #import "ViewController.h" #import "AudioEngine.h" #import +#include #include "base/display.h" #include "base/timeutil.h" @@ -38,6 +39,8 @@ public: IOSDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + bool success = draw_->CreatePresets(); + assert(success); } ~IOSDummyGraphicsContext() { delete draw_;