renderer_vulkan: Fix texture allocation and destruction logic
This commit is contained in:
@ -56,10 +56,6 @@ protected:
|
|||||||
constexpr NonCopyable() = default;
|
constexpr NonCopyable() = default;
|
||||||
~NonCopyable() = default;
|
~NonCopyable() = default;
|
||||||
|
|
||||||
// Enable std::move operations
|
|
||||||
NonCopyable(NonCopyable&&) = default;
|
|
||||||
NonCopyable& operator=(NonCopyable&&) = default;
|
|
||||||
|
|
||||||
NonCopyable(const NonCopyable&) = delete;
|
NonCopyable(const NonCopyable&) = delete;
|
||||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||||
};
|
};
|
||||||
|
@ -131,35 +131,6 @@ std::vector<const char*> RequiredExtensions(Frontend::WindowSystemType window_ty
|
|||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
|
|
||||||
* corner and (width, height) on the lower-bottom.
|
|
||||||
*
|
|
||||||
* The projection part of the matrix is trivial, hence these operations are represented
|
|
||||||
* by a 3x2 matrix.
|
|
||||||
*
|
|
||||||
* @param flipped Whether the frame should be flipped upside down.
|
|
||||||
*/
|
|
||||||
static glm::mat3x2 MakeOrthographicMatrix(const float width, const float height, bool flipped) {
|
|
||||||
glm::mat3x2 matrix; // Laid out in column-major order
|
|
||||||
|
|
||||||
// Last matrix row is implicitly assumed to be [0, 0, 1].
|
|
||||||
if (flipped) {
|
|
||||||
// clang-format off
|
|
||||||
matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f;
|
|
||||||
matrix[0][1] = 0.f; matrix[1][1] = 2.f / height; matrix[2][1] = -1.f;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// clang-format off
|
|
||||||
matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f;
|
|
||||||
matrix[1][1] = 0.f; matrix[1][1] = -2.f / height; matrix[2][1] = 1.f;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
return matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
||||||
: RendererBase{window} {
|
: RendererBase{window} {
|
||||||
|
|
||||||
@ -304,13 +275,13 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
|
|||||||
|
|
||||||
switch (screen.format) {
|
switch (screen.format) {
|
||||||
case GPU::Regs::PixelFormat::RGBA8:
|
case GPU::Regs::PixelFormat::RGBA8:
|
||||||
texture_info.format = vk::Format::eR8G8B8A8Srgb;
|
texture_info.format = vk::Format::eR8G8B8A8Unorm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPU::Regs::PixelFormat::RGB8:
|
case GPU::Regs::PixelFormat::RGB8:
|
||||||
// Note that the texture will actually give us an RGBA8 image because next to no modern hardware supports RGB formats.
|
// Note that the texture will actually give us an RGBA8 image because next to no modern hardware supports RGB formats.
|
||||||
// The pixels will be converted automatically by Upload()
|
// The pixels will be converted automatically by Upload()
|
||||||
texture_info.format = vk::Format::eR8G8B8Srgb;
|
texture_info.format = vk::Format::eR8G8B8Unorm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPU::Regs::PixelFormat::RGB565:
|
case GPU::Regs::PixelFormat::RGB565:
|
||||||
@ -521,7 +492,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||||||
// Set projection matrix
|
// Set projection matrix
|
||||||
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
|
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
|
||||||
static_cast<float>(layout.height), 0.0f,
|
static_cast<float>(layout.height), 0.0f,
|
||||||
-1.f, 1.f));
|
0.f, 1.f));
|
||||||
const bool stereo_single_screen = false
|
const bool stereo_single_screen = false
|
||||||
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
|
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
|
||||||
Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
|
Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
|
||||||
@ -535,7 +506,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||||||
auto& image = swapchain->GetCurrentImage();
|
auto& image = swapchain->GetCurrentImage();
|
||||||
auto& state = VulkanState::Get();
|
auto& state = VulkanState::Get();
|
||||||
|
|
||||||
state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear);
|
state.BeginRendering(&image, nullptr, false, clear_color, vk::AttachmentLoadOp::eClear);
|
||||||
state.SetPresentTextures(screen_infos[0].display_texture->GetView(),
|
state.SetPresentTextures(screen_infos[0].display_texture->GetView(),
|
||||||
screen_infos[1].display_texture->GetView(),
|
screen_infos[1].display_texture->GetView(),
|
||||||
screen_infos[2].display_texture->GetView());
|
screen_infos[2].display_texture->GetView());
|
||||||
@ -727,7 +698,7 @@ VideoCore::ResultStatus RendererVulkan::Init() {
|
|||||||
auto surface = CreateSurface(instance, render_window);
|
auto surface = CreateSurface(instance, render_window);
|
||||||
g_vk_instace = std::make_unique<VKInstance>();
|
g_vk_instace = std::make_unique<VKInstance>();
|
||||||
g_vk_task_scheduler = std::make_unique<VKTaskScheduler>();
|
g_vk_task_scheduler = std::make_unique<VKTaskScheduler>();
|
||||||
g_vk_instace->Create(instance, physical_devices[0], surface, true);
|
g_vk_instace->Create(instance, physical_devices[1], surface, true);
|
||||||
g_vk_task_scheduler->Create();
|
g_vk_task_scheduler->Create();
|
||||||
|
|
||||||
//auto& layout = render_window.GetFramebufferLayout();
|
//auto& layout = render_window.GetFramebufferLayout();
|
||||||
|
@ -28,12 +28,12 @@ void VKBuffer::Create(const VKBuffer::Info& info) {
|
|||||||
auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, info.properties);
|
auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, info.properties);
|
||||||
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
|
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
|
||||||
|
|
||||||
buffer_memory = device.allocateMemory(alloc_info);
|
memory = device.allocateMemory(alloc_info);
|
||||||
device.bindBufferMemory(buffer, buffer_memory, 0);
|
device.bindBufferMemory(buffer, memory, 0);
|
||||||
|
|
||||||
// Optionally map the buffer to CPU memory
|
// Optionally map the buffer to CPU memory
|
||||||
if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) {
|
if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) {
|
||||||
host_ptr = device.mapMemory(buffer_memory, 0, info.size);
|
host_ptr = device.mapMemory(memory, 0, info.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& format : info.view_formats) {
|
for (auto& format : info.view_formats) {
|
||||||
@ -51,13 +51,16 @@ void VKBuffer::Recreate() {
|
|||||||
void VKBuffer::Destroy() {
|
void VKBuffer::Destroy() {
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
if (host_ptr != nullptr) {
|
if (host_ptr != nullptr) {
|
||||||
g_vk_instace->GetDevice().unmapMemory(buffer_memory);
|
g_vk_instace->GetDevice().unmapMemory(memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto deleter = [this]() {
|
auto deleter = [buffer = buffer,
|
||||||
|
memory = memory,
|
||||||
|
view_count = view_count,
|
||||||
|
views = views]() {
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
device.destroyBuffer(buffer);
|
device.destroyBuffer(buffer);
|
||||||
device.freeMemory(buffer_memory);
|
device.freeMemory(memory);
|
||||||
|
|
||||||
for (u32 i = 0; i < view_count; i++) {
|
for (u32 i = 0; i < view_count; i++) {
|
||||||
device.destroyBufferView(views[i]);
|
device.destroyBufferView(views[i]);
|
||||||
|
@ -54,7 +54,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
Info buffer_info;
|
Info buffer_info;
|
||||||
vk::Buffer buffer;
|
vk::Buffer buffer;
|
||||||
vk::DeviceMemory buffer_memory;
|
vk::DeviceMemory memory;
|
||||||
void* host_ptr = nullptr;
|
void* host_ptr = nullptr;
|
||||||
std::array<vk::BufferView, MAX_BUFFER_VIEWS> views;
|
std::array<vk::BufferView, MAX_BUFFER_VIEWS> views;
|
||||||
u32 view_count{};
|
u32 view_count{};
|
||||||
|
@ -7,22 +7,21 @@
|
|||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
|
|
||||||
#if defined(VK_EXT_color_write_enable)
|
|
||||||
PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT;
|
|
||||||
#endif /* defined(VK_EXT_color_write_enable) */
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
std::unique_ptr<VKInstance> g_vk_instace;
|
std::unique_ptr<VKInstance> g_vk_instace;
|
||||||
|
|
||||||
VKInstance::~VKInstance() {
|
VKInstance::~VKInstance() {
|
||||||
|
device.waitIdle();
|
||||||
|
|
||||||
|
device.destroy();
|
||||||
|
instance.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKInstance::Create(vk::Instance instance, vk::PhysicalDevice physical_device,
|
bool VKInstance::Create(vk::Instance new_instance, vk::PhysicalDevice gpu,
|
||||||
vk::SurfaceKHR surface, bool enable_validation_layer) {
|
vk::SurfaceKHR surface, bool enable_validation_layer) {
|
||||||
this->instance = instance;
|
instance = new_instance;
|
||||||
this->physical_device = physical_device;
|
physical_device = gpu;
|
||||||
|
|
||||||
// Get physical device limits
|
// Get physical device limits
|
||||||
device_limits = physical_device.getProperties().limits;
|
device_limits = physical_device.getProperties().limits;
|
||||||
@ -98,15 +97,11 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create logical device
|
// Create logical device
|
||||||
device = physical_device.createDeviceUnique(device_info);
|
device = physical_device.createDevice(device_info);
|
||||||
|
|
||||||
#if defined(VK_EXT_color_write_enable)
|
|
||||||
ptr_vkCmdSetColorWriteEnableEXT = reinterpret_cast<PFN_vkCmdSetColorWriteEnableEXT>(device->getProcAddr("vkCmdSetColorWriteEnableEXT"));
|
|
||||||
#endif /* defined(VK_EXT_color_write_enable) */
|
|
||||||
|
|
||||||
// Grab the graphics and present queues.
|
// Grab the graphics and present queues.
|
||||||
graphics_queue = device->getQueue(graphics_queue_family_index, 0);
|
graphics_queue = device.getQueue(graphics_queue_family_index, 0);
|
||||||
present_queue = device->getQueue(present_queue_family_index, 0);
|
present_queue = device.getQueue(present_queue_family_index, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -195,7 +190,3 @@ bool VKInstance::FindExtensions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
||||||
void vkCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables) {
|
|
||||||
ptr_vkCmdSetColorWriteEnableEXT(commandBuffer, attachmentCount, pColorWriteEnables);
|
|
||||||
}
|
|
||||||
|
@ -22,8 +22,9 @@ public:
|
|||||||
bool Create(vk::Instance instance, vk::PhysicalDevice gpu,
|
bool Create(vk::Instance instance, vk::PhysicalDevice gpu,
|
||||||
vk::SurfaceKHR surface, bool enable_validation_layer);
|
vk::SurfaceKHR surface, bool enable_validation_layer);
|
||||||
|
|
||||||
vk::Device GetDevice() const { return device.get(); }
|
vk::Device GetDevice() const { return device; }
|
||||||
vk::PhysicalDevice GetPhysicalDevice() const { return physical_device; }
|
vk::PhysicalDevice GetPhysicalDevice() const { return physical_device; }
|
||||||
|
vk::Instance GetInstance() const { return instance; }
|
||||||
|
|
||||||
/// Retrieve queue information
|
/// Retrieve queue information
|
||||||
u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; }
|
u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; }
|
||||||
@ -46,9 +47,9 @@ public:
|
|||||||
vk::Queue present_queue, graphics_queue;
|
vk::Queue present_queue, graphics_queue;
|
||||||
|
|
||||||
// Core vulkan objects
|
// Core vulkan objects
|
||||||
vk::Instance instance;
|
|
||||||
vk::PhysicalDevice physical_device;
|
vk::PhysicalDevice physical_device;
|
||||||
vk::UniqueDevice device;
|
vk::Instance instance;
|
||||||
|
vk::Device device;
|
||||||
|
|
||||||
// Extensions and features
|
// Extensions and features
|
||||||
std::vector<const char*> extensions;
|
std::vector<const char*> extensions;
|
||||||
@ -67,7 +68,3 @@ public:
|
|||||||
extern std::unique_ptr<VKInstance> g_vk_instace;
|
extern std::unique_ptr<VKInstance> g_vk_instace;
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
||||||
#if defined(VK_EXT_color_write_enable)
|
|
||||||
extern PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT;
|
|
||||||
#endif /* defined(VK_EXT_color_write_enable) */
|
|
||||||
|
@ -367,7 +367,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
|||||||
state.SetScissor(scissor);
|
state.SetScissor(scissor);
|
||||||
|
|
||||||
// Bind the framebuffer surfaces
|
// Bind the framebuffer surfaces
|
||||||
state.BeginRendering(color_surface->texture, depth_surface->texture, true);
|
state.BeginRendering(color_surface != nullptr ? &color_surface->texture : nullptr,
|
||||||
|
depth_surface != nullptr ? &depth_surface->texture : nullptr, true);
|
||||||
state.ApplyRenderState(Pica::g_state.regs);
|
state.ApplyRenderState(Pica::g_state.regs);
|
||||||
state.SetVertexBuffer(vertex_buffer, 0);
|
state.SetVertexBuffer(vertex_buffer, 0);
|
||||||
|
|
||||||
@ -399,8 +400,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.EndRendering();
|
state.EndRendering();
|
||||||
color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
|
||||||
depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
if (color_surface) {
|
||||||
|
color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth_surface) {
|
||||||
|
depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
|
}
|
||||||
|
|
||||||
g_vk_task_scheduler->Submit();
|
g_vk_task_scheduler->Submit();
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ using SurfaceType = SurfaceParams::SurfaceType;
|
|||||||
using PixelFormat = SurfaceParams::PixelFormat;
|
using PixelFormat = SurfaceParams::PixelFormat;
|
||||||
|
|
||||||
static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
|
static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
|
||||||
vk::Format::eR8G8B8A8Srgb, // RGBA8
|
vk::Format::eR8G8B8A8Unorm, // RGBA8
|
||||||
vk::Format::eR8G8B8Srgb, // RGB8
|
vk::Format::eR8G8B8Unorm, // RGB8
|
||||||
vk::Format::eR5G5B5A1UnormPack16, // RGB5A1
|
vk::Format::eR5G5B5A1UnormPack16, // RGB5A1
|
||||||
vk::Format::eR5G6B5UnormPack16, // RGB565
|
vk::Format::eR5G6B5UnormPack16, // RGB565
|
||||||
vk::Format::eR4G4B4A4UnormPack16, // RGBA4
|
vk::Format::eR4G4B4A4UnormPack16, // RGBA4
|
||||||
@ -66,7 +66,7 @@ vk::Format GetFormatTuple(PixelFormat pixel_format) {
|
|||||||
ASSERT(tuple_idx < depth_format_tuples.size());
|
ASSERT(tuple_idx < depth_format_tuples.size());
|
||||||
return depth_format_tuples[tuple_idx];
|
return depth_format_tuples[tuple_idx];
|
||||||
}
|
}
|
||||||
return vk::Format::eR8G8B8A8Srgb;
|
return vk::Format::eR8G8B8A8Unorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Map, typename Interval>
|
template <typename Map, typename Interval>
|
||||||
@ -293,6 +293,7 @@ static bool FillSurface(const Surface& surface, std::array<u8, 4> fill_buffer,
|
|||||||
if (surface->GetScaledRect() != rect) {
|
if (surface->GetScaledRect() != rect) {
|
||||||
// TODO: use vkCmdClearAttachments to clear subrects
|
// TODO: use vkCmdClearAttachments to clear subrects
|
||||||
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
|
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1};
|
vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1};
|
||||||
@ -327,12 +328,36 @@ static bool FillSurface(const Surface& surface, std::array<u8, 4> fill_buffer,
|
|||||||
|
|
||||||
cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
|
cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
|
||||||
color, image_range);
|
color, image_range);
|
||||||
|
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SurfaceParams::SurfaceType::Depth:
|
||||||
|
case SurfaceParams::SurfaceType::DepthStencil: {
|
||||||
|
auto& texture = surface->texture;
|
||||||
|
texture.Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
|
||||||
|
|
||||||
|
u32 value_32bit = 0;
|
||||||
|
vk::ClearDepthStencilValue clear_value;
|
||||||
|
|
||||||
|
if (surface->pixel_format == SurfaceParams::PixelFormat::D16) {
|
||||||
|
std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u16));
|
||||||
|
clear_value.depth = value_32bit / 65535.0f; // 2^16 - 1
|
||||||
|
} else if (surface->pixel_format == SurfaceParams::PixelFormat::D24) {
|
||||||
|
std::memcpy(&value_32bit, fill_buffer.data(), 3);
|
||||||
|
clear_value.depth = value_32bit / 16777215.0f; // 2^24 - 1
|
||||||
|
} else {
|
||||||
|
std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u32));
|
||||||
|
clear_value.depth = (value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
||||||
|
clear_value.stencil = value_32bit >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdbuffer.clearDepthStencilImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
|
||||||
|
clear_value, image_range);
|
||||||
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Render_Vulkan, "non-color fills not implemented");
|
LOG_CRITICAL(Render_Vulkan, "Unsupported fill operation requested!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,14 +369,14 @@ static vk::Rect2D FromRect(Common::Rectangle<u32> rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||||
VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Format format,
|
void RasterizerCacheVulkan::AllocateTexture(VKTexture& target, SurfaceType type, vk::Format format,
|
||||||
u32 width, u32 height) {
|
u32 width, u32 height) {
|
||||||
// First check if the texture can be recycled
|
// First check if the texture can be recycled
|
||||||
auto recycled_tex = host_texture_recycler.find({format, width, height});
|
auto recycled_tex = host_texture_recycler.find({format, width, height});
|
||||||
if (recycled_tex != host_texture_recycler.end()) {
|
if (recycled_tex != host_texture_recycler.end()) {
|
||||||
VKTexture texture = std::move(recycled_tex->second);
|
target = std::move(recycled_tex->second);
|
||||||
host_texture_recycler.erase(recycled_tex);
|
host_texture_recycler.erase(recycled_tex);
|
||||||
return texture;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto GetUsage = [](SurfaceType type) {
|
auto GetUsage = [](SurfaceType type) {
|
||||||
@ -378,7 +403,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
|
|||||||
|
|
||||||
// Otherwise create a brand new texture
|
// Otherwise create a brand new texture
|
||||||
u32 levels = std::log2(std::max(width, height)) + 1;
|
u32 levels = std::log2(std::max(width, height)) + 1;
|
||||||
VKTexture::Info texture_info = {
|
VKTexture::Info texture_info{
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
.format = format,
|
.format = format,
|
||||||
@ -388,13 +413,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
|
|||||||
.levels = levels
|
.levels = levels
|
||||||
};
|
};
|
||||||
|
|
||||||
VKTexture texture;
|
|
||||||
texture.Create(texture_info);
|
|
||||||
|
|
||||||
auto cmdbuffer = g_vk_task_scheduler->GetUploadCommandBuffer();
|
auto cmdbuffer = g_vk_task_scheduler->GetUploadCommandBuffer();
|
||||||
texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
|
||||||
|
|
||||||
return texture;
|
target.Destroy();
|
||||||
|
target.Create(texture_info);
|
||||||
|
target.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedSurface::~CachedSurface() {
|
CachedSurface::~CachedSurface() {
|
||||||
@ -1431,8 +1454,8 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) {
|
|||||||
static_cast<SurfaceParams&>(*surface) = params;
|
static_cast<SurfaceParams&>(*surface) = params;
|
||||||
|
|
||||||
surface->invalid_regions.insert(surface->GetInterval());
|
surface->invalid_regions.insert(surface->GetInterval());
|
||||||
surface->texture = AllocateSurfaceTexture(params.type, GetFormatTuple(surface->pixel_format),
|
AllocateTexture(surface->texture, params.type, GetFormatTuple(surface->pixel_format),
|
||||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
surface->GetScaledWidth(), surface->GetScaledHeight());
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ private:
|
|||||||
std::recursive_mutex mutex;
|
std::recursive_mutex mutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VKTexture AllocateSurfaceTexture(SurfaceParams::SurfaceType type, vk::Format format,
|
void AllocateTexture(VKTexture& target, SurfaceParams::SurfaceType type, vk::Format format,
|
||||||
u32 width, u32 height);
|
u32 width, u32 height);
|
||||||
std::unique_ptr<FormatReinterpreterVulkan> format_reinterpreter;
|
std::unique_ptr<FormatReinterpreterVulkan> format_reinterpreter;
|
||||||
};
|
};
|
||||||
|
@ -1539,6 +1539,7 @@ vec4 secondary_fragment_color = vec4(0.0);
|
|||||||
// do our own transformation according to PICA specification.
|
// do our own transformation according to PICA specification.
|
||||||
out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n"
|
out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n"
|
||||||
"float depth = z_over_w * depth_scale + depth_offset;\n";
|
"float depth = z_over_w * depth_scale + depth_offset;\n";
|
||||||
|
//out += "if (gl_FragCoord.z == 0) { color = vec4(1.0, 0.0, 0.0, 1.0); return; }\n";
|
||||||
if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
|
if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
|
||||||
out += "depth /= gl_FragCoord.w;\n";
|
out += "depth /= gl_FragCoord.w;\n";
|
||||||
}
|
}
|
||||||
@ -1655,11 +1656,14 @@ void main() {
|
|||||||
texcoord0_w = vert_texcoord0_w;
|
texcoord0_w = vert_texcoord0_w;
|
||||||
normquat = vert_normquat;
|
normquat = vert_normquat;
|
||||||
view = vert_view;
|
view = vert_view;
|
||||||
gl_Position = vert_position + vec4(0.0, 0.0, 1.0, 0.0);
|
|
||||||
#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
|
// Convert OpenGL [-1, 1] to Vulkan [0, 1]
|
||||||
//gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
|
vec4 position = vert_position;
|
||||||
//gl_ClipDistance[1] = dot(clip_coef, vert_position);
|
position.z = (position.z + 1) * 0.5;
|
||||||
#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
|
|
||||||
|
gl_Position = position;
|
||||||
|
gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
|
||||||
|
gl_ClipDistance[1] = dot(clip_coef, vert_position);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "video_core/renderer_vulkan/vk_state.h"
|
#include "video_core/renderer_vulkan/vk_state.h"
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_rasterizer_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
|
|||||||
VKTexture::Info info{
|
VKTexture::Info info{
|
||||||
.width = 1,
|
.width = 1,
|
||||||
.height = 1,
|
.height = 1,
|
||||||
.format = vk::Format::eR8G8B8A8Srgb,
|
.format = vk::Format::eR8G8B8A8Unorm,
|
||||||
.type = vk::ImageType::e2D,
|
.type = vk::ImageType::e2D,
|
||||||
.view_type = vk::ImageViewType::e2D,
|
.view_type = vk::ImageViewType::e2D,
|
||||||
.usage = vk::ImageUsageFlagBits::eSampled |
|
.usage = vk::ImageUsageFlagBits::eSampled |
|
||||||
@ -124,9 +125,12 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
|
|||||||
|
|
||||||
VulkanState::~VulkanState() {
|
VulkanState::~VulkanState() {
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
|
device.waitIdle();
|
||||||
|
|
||||||
// Destroy vertex shader
|
// Destroy vertex shader
|
||||||
device.destroyShaderModule(render_vertex_shader);
|
device.destroyShaderModule(render_vertex_shader);
|
||||||
|
device.destroyShaderModule(present_vertex_shader);
|
||||||
|
device.destroyShaderModule(present_fragment_shader);
|
||||||
|
|
||||||
// Destroy pipeline layouts
|
// Destroy pipeline layouts
|
||||||
device.destroyPipelineLayout(render_pipeline_layout);
|
device.destroyPipelineLayout(render_pipeline_layout);
|
||||||
@ -227,7 +231,7 @@ void VulkanState::UnbindTexture(u32 unit) {
|
|||||||
descriptors_dirty = true;
|
descriptors_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> depth, bool update_pipeline_formats,
|
void VulkanState::BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats,
|
||||||
vk::ClearColorValue color_clear, vk::AttachmentLoadOp color_load_op,
|
vk::ClearColorValue color_clear, vk::AttachmentLoadOp color_load_op,
|
||||||
vk::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear,
|
vk::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear,
|
||||||
vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op,
|
vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op,
|
||||||
@ -236,38 +240,37 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
|
|||||||
EndRendering();
|
EndRendering();
|
||||||
|
|
||||||
// Make sure attachments are in optimal layout
|
// Make sure attachments are in optimal layout
|
||||||
vk::RenderingInfo render_info{{}, color->get().GetArea(), 1, {}};
|
vk::RenderingInfo render_info{{}, {}, 1, {}};
|
||||||
std::array<vk::RenderingAttachmentInfo, 3> infos{};
|
std::array<vk::RenderingAttachmentInfo, 3> infos{};
|
||||||
|
|
||||||
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
|
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
|
||||||
if (color.has_value()) {
|
if (color != nullptr) {
|
||||||
auto& image = color->get();
|
color->Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
|
||||||
image.Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
|
|
||||||
|
|
||||||
infos[0] = vk::RenderingAttachmentInfo{
|
infos[0] = vk::RenderingAttachmentInfo{
|
||||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
color->GetView(), color->GetLayout(), {}, {}, {},
|
||||||
color_load_op, color_store_op, color_clear
|
color_load_op, color_store_op, color_clear
|
||||||
};
|
};
|
||||||
|
|
||||||
render_info.colorAttachmentCount = 1;
|
render_info.colorAttachmentCount = 1;
|
||||||
render_info.pColorAttachments = &infos[0];
|
render_info.pColorAttachments = &infos[0];
|
||||||
|
render_info.renderArea = color->GetArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth.has_value()) {
|
if (depth != nullptr) {
|
||||||
auto& image = depth->get();
|
depth->Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||||
image.Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
|
||||||
|
|
||||||
infos[1] = vk::RenderingAttachmentInfo{
|
infos[1] = vk::RenderingAttachmentInfo{
|
||||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
depth->GetView(), depth->GetLayout(), {}, {}, {},
|
||||||
depth_load_op, depth_store_op, depth_clear
|
depth_load_op, depth_store_op, depth_clear
|
||||||
};
|
};
|
||||||
|
|
||||||
render_info.pDepthAttachment = &infos[1];
|
render_info.pDepthAttachment = &infos[1];
|
||||||
|
|
||||||
|
|
||||||
if (IsStencil(image.GetFormat())) {
|
if (IsStencil(depth->GetFormat())) {
|
||||||
infos[2] = vk::RenderingAttachmentInfo{
|
infos[2] = vk::RenderingAttachmentInfo{
|
||||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
depth->GetView(), depth->GetLayout(), {}, {}, {},
|
||||||
stencil_load_op, stencil_store_op, depth_clear
|
stencil_load_op, stencil_store_op, depth_clear
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -276,8 +279,12 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (update_pipeline_formats) {
|
if (update_pipeline_formats) {
|
||||||
render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined;
|
render_pipeline_key.color = color != nullptr ?
|
||||||
render_pipeline_key.depth_stencil = depth.has_value() ? depth->get().GetFormat() : vk::Format::eUndefined;
|
color->GetFormat() :
|
||||||
|
vk::Format::eUndefined;
|
||||||
|
render_pipeline_key.depth_stencil = depth != nullptr ?
|
||||||
|
depth->GetFormat() :
|
||||||
|
vk::Format::eUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin rendering
|
// Begin rendering
|
||||||
|
@ -17,9 +17,6 @@ namespace Vulkan {
|
|||||||
|
|
||||||
constexpr u32 DESCRIPTOR_SET_COUNT = 4;
|
constexpr u32 DESCRIPTOR_SET_COUNT = 4;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using OptRef = std::optional<std::reference_wrapper<T>>;
|
|
||||||
|
|
||||||
struct DrawInfo {
|
struct DrawInfo {
|
||||||
glm::mat4 modelview;
|
glm::mat4 modelview;
|
||||||
glm::vec4 i_resolution;
|
glm::vec4 i_resolution;
|
||||||
@ -93,7 +90,7 @@ public:
|
|||||||
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
|
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
|
||||||
|
|
||||||
/// Rendering
|
/// Rendering
|
||||||
void BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> depth, bool update_pipeline_formats = false,
|
void BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats = false,
|
||||||
vk::ClearColorValue color_clear = {},
|
vk::ClearColorValue color_clear = {},
|
||||||
vk::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad,
|
vk::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad,
|
||||||
vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore,
|
vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore,
|
||||||
|
@ -19,9 +19,13 @@ VKSwapChain::VKSwapChain(vk::SurfaceKHR surface_) : surface(surface_) {
|
|||||||
|
|
||||||
VKSwapChain::~VKSwapChain() {
|
VKSwapChain::~VKSwapChain() {
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
|
auto instance = g_vk_instace->GetInstance();
|
||||||
device.waitIdle();
|
device.waitIdle();
|
||||||
|
|
||||||
|
device.destroySemaphore(render_finished);
|
||||||
|
device.destroySemaphore(image_available);
|
||||||
device.destroySwapchainKHR(swapchain);
|
device.destroySwapchainKHR(swapchain);
|
||||||
|
instance.destroySurfaceKHR(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) {
|
bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||||
@ -59,11 +63,11 @@ bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) {
|
|||||||
|
|
||||||
// Create sync objects if not already created
|
// Create sync objects if not already created
|
||||||
if (!image_available) {
|
if (!image_available) {
|
||||||
image_available = device.createSemaphoreUnique({});
|
image_available = device.createSemaphore({});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!render_finished) {
|
if (!render_finished) {
|
||||||
render_finished = device.createSemaphoreUnique({});
|
render_finished = device.createSemaphore({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create framebuffer and image views
|
// Create framebuffer and image views
|
||||||
@ -78,7 +82,7 @@ constexpr u64 ACQUIRE_TIMEOUT = 1000000000;
|
|||||||
|
|
||||||
void VKSwapChain::AcquireNextImage() {
|
void VKSwapChain::AcquireNextImage() {
|
||||||
auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT,
|
auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT,
|
||||||
image_available.get(), VK_NULL_HANDLE,
|
image_available, VK_NULL_HANDLE,
|
||||||
&image_index);
|
&image_index);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case vk::Result::eSuccess:
|
case vk::Result::eSuccess:
|
||||||
@ -98,7 +102,7 @@ void VKSwapChain::AcquireNextImage() {
|
|||||||
void VKSwapChain::Present() {
|
void VKSwapChain::Present() {
|
||||||
const auto present_queue = g_vk_instace->GetPresentQueue();
|
const auto present_queue = g_vk_instace->GetPresentQueue();
|
||||||
|
|
||||||
vk::PresentInfoKHR present_info(render_finished.get(), swapchain, image_index);
|
vk::PresentInfoKHR present_info(render_finished, swapchain, image_index);
|
||||||
vk::Result result = present_queue.presentKHR(present_info);
|
vk::Result result = present_queue.presentKHR(present_info);
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
@ -42,8 +42,8 @@ public:
|
|||||||
vk::SurfaceKHR GetSurface() const { return surface; }
|
vk::SurfaceKHR GetSurface() const { return surface; }
|
||||||
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
|
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
|
||||||
vk::SwapchainKHR GetSwapChain() const { return swapchain; }
|
vk::SwapchainKHR GetSwapChain() const { return swapchain; }
|
||||||
const vk::Semaphore& GetAvailableSemaphore() const { return image_available.get(); }
|
const vk::Semaphore& GetAvailableSemaphore() const { return image_available; }
|
||||||
const vk::Semaphore& GetRenderSemaphore() const { return render_finished.get(); }
|
const vk::Semaphore& GetRenderSemaphore() const { return render_finished; }
|
||||||
VKTexture& GetCurrentImage() { return swapchain_images[image_index]; }
|
VKTexture& GetCurrentImage() { return swapchain_images[image_index]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -53,7 +53,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
SwapChainDetails details{};
|
SwapChainDetails details{};
|
||||||
vk::SurfaceKHR surface;
|
vk::SurfaceKHR surface;
|
||||||
vk::UniqueSemaphore image_available, render_finished;
|
vk::Semaphore image_available, render_finished;
|
||||||
bool vsync_enabled{false}, is_outdated{true}, is_suboptimal{true};
|
bool vsync_enabled{false}, is_outdated{true}, is_suboptimal{true};
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain{VK_NULL_HANDLE};
|
vk::SwapchainKHR swapchain{VK_NULL_HANDLE};
|
||||||
|
@ -11,10 +11,11 @@
|
|||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
VKTaskScheduler::~VKTaskScheduler() {
|
VKTaskScheduler::~VKTaskScheduler() {
|
||||||
SyncToGPU();
|
|
||||||
|
|
||||||
// Destroy Vulkan resources
|
// Destroy Vulkan resources
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
|
device.waitIdle();
|
||||||
|
|
||||||
|
SyncToGPU();
|
||||||
device.destroyCommandPool(command_pool);
|
device.destroyCommandPool(command_pool);
|
||||||
device.destroySemaphore(timeline);
|
device.destroySemaphore(timeline);
|
||||||
|
|
||||||
@ -63,7 +64,8 @@ bool VKTaskScheduler::Create() {
|
|||||||
.size = STAGING_BUFFER_SIZE,
|
.size = STAGING_BUFFER_SIZE,
|
||||||
.properties = vk::MemoryPropertyFlagBits::eHostVisible |
|
.properties = vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
vk::MemoryPropertyFlagBits::eHostCoherent,
|
vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||||
.usage = vk::BufferUsageFlagBits::eTransferSrc
|
.usage = vk::BufferUsageFlagBits::eTransferSrc |
|
||||||
|
vk::BufferUsageFlagBits::eTransferDst
|
||||||
};
|
};
|
||||||
|
|
||||||
// Should be enough for a single frame
|
// Should be enough for a single frame
|
||||||
@ -131,6 +133,7 @@ void VKTaskScheduler::SyncToGPU(u64 task_index) {
|
|||||||
for (auto& func : task.cleanups) {
|
for (auto& func : task.cleanups) {
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
|
task.cleanups.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,10 @@ static int BytesPerPixel(vk::Format format) {
|
|||||||
case vk::Format::eD32Sfloat:
|
case vk::Format::eD32Sfloat:
|
||||||
case vk::Format::eB8G8R8A8Unorm:
|
case vk::Format::eB8G8R8A8Unorm:
|
||||||
case vk::Format::eR8G8B8A8Uint:
|
case vk::Format::eR8G8B8A8Uint:
|
||||||
case vk::Format::eR8G8B8A8Srgb:
|
case vk::Format::eR8G8B8A8Unorm:
|
||||||
case vk::Format::eD24UnormS8Uint:
|
case vk::Format::eD24UnormS8Uint:
|
||||||
return 4;
|
return 4;
|
||||||
case vk::Format::eR8G8B8Uint:
|
case vk::Format::eR8G8B8Unorm:
|
||||||
case vk::Format::eR8G8B8Srgb:
|
case vk::Format::eR8G8B8Srgb:
|
||||||
return 3;
|
return 3;
|
||||||
case vk::Format::eR5G6B5UnormPack16:
|
case vk::Format::eR5G6B5UnormPack16:
|
||||||
@ -58,15 +58,41 @@ VKTexture::~VKTexture() {
|
|||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VKTexture::VKTexture(VKTexture&& other) noexcept {
|
||||||
|
info = std::exchange(other.info, Info{});
|
||||||
|
texture = std::exchange(other.texture, VK_NULL_HANDLE);
|
||||||
|
aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone);
|
||||||
|
view = std::exchange(other.view, VK_NULL_HANDLE);
|
||||||
|
memory = std::exchange(other.memory, VK_NULL_HANDLE);
|
||||||
|
image_size = std::exchange(other.image_size, 0);
|
||||||
|
adopted = std::exchange(other.adopted, false);
|
||||||
|
is_rgb = std::exchange(other.is_rgb, false);
|
||||||
|
is_d24s8 = std::exchange(other.is_d24s8, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKTexture& VKTexture::operator=(VKTexture&& other) noexcept {
|
||||||
|
Destroy();
|
||||||
|
info = std::exchange(other.info, Info{});
|
||||||
|
texture = std::exchange(other.texture, VK_NULL_HANDLE);
|
||||||
|
aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone);
|
||||||
|
view = std::exchange(other.view, VK_NULL_HANDLE);
|
||||||
|
memory = std::exchange(other.memory, VK_NULL_HANDLE);
|
||||||
|
image_size = std::exchange(other.image_size, 0);
|
||||||
|
adopted = std::exchange(other.adopted, false);
|
||||||
|
is_rgb = std::exchange(other.is_rgb, false);
|
||||||
|
is_d24s8 = std::exchange(other.is_d24s8, false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void VKTexture::Create(const Info& create_info) {
|
void VKTexture::Create(const Info& create_info) {
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
info = create_info;
|
info = create_info;
|
||||||
|
|
||||||
// Emulate RGB8 format with RGBA8
|
// Emulate RGB8 format with RGBA8
|
||||||
is_rgb = false;
|
is_rgb = false;
|
||||||
if (info.format == vk::Format::eR8G8B8Srgb) {
|
if (info.format == vk::Format::eR8G8B8Unorm) {
|
||||||
is_rgb = true;
|
is_rgb = true;
|
||||||
info.format = vk::Format::eR8G8B8A8Srgb;
|
info.format = vk::Format::eR8G8B8A8Unorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_d24s8 = false;
|
is_d24s8 = false;
|
||||||
@ -136,7 +162,9 @@ void VKTexture::Destroy() {
|
|||||||
auto& state = VulkanState::Get();
|
auto& state = VulkanState::Get();
|
||||||
state.UnbindTexture(*this);
|
state.UnbindTexture(*this);
|
||||||
|
|
||||||
auto deleter = [this]() {
|
auto deleter = [texture = texture,
|
||||||
|
view = view,
|
||||||
|
memory = memory]() {
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
if (texture) {
|
if (texture) {
|
||||||
std::cout << "Surface destroyed!\n";
|
std::cout << "Surface destroyed!\n";
|
||||||
@ -154,7 +182,7 @@ void VKTexture::Destroy() {
|
|||||||
// If the image was adopted (probably from the swapchain) then only
|
// If the image was adopted (probably from the swapchain) then only
|
||||||
// destroy the view
|
// destroy the view
|
||||||
if (adopted) {
|
if (adopted) {
|
||||||
g_vk_task_scheduler->Schedule([this](){
|
g_vk_task_scheduler->Schedule([view = view](){
|
||||||
auto device = g_vk_instace->GetDevice();
|
auto device = g_vk_instace->GetDevice();
|
||||||
device.destroyImageView(view);
|
device.destroyImageView(view);
|
||||||
});
|
});
|
||||||
@ -281,19 +309,32 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
|
|||||||
std::memcpy(buffer, pixels.data(), pixels.size());
|
std::memcpy(buffer, pixels.data(), pixels.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::BufferImageCopy copy_region{
|
std::array<vk::BufferImageCopy, 2> copy_regions;
|
||||||
|
u32 region_count = 1;
|
||||||
|
|
||||||
|
copy_regions[0] = vk::BufferImageCopy{
|
||||||
offset, row_length, region.extent.height,
|
offset, row_length, region.extent.height,
|
||||||
{aspect, level, layer, 1},
|
{aspect, level, layer, 1},
|
||||||
{region.offset.x, region.offset.y, 0},
|
{region.offset.x, region.offset.y, 0},
|
||||||
{region.extent.width, region.extent.height, 1}
|
{region.extent.width, region.extent.height, 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (aspect & vk::ImageAspectFlagBits::eDepth &&
|
||||||
|
aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
|
// Copying both depth and stencil requires two seperate regions
|
||||||
|
copy_regions[1] = copy_regions[0];
|
||||||
|
copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
||||||
|
copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
|
||||||
|
|
||||||
|
region_count++;
|
||||||
|
}
|
||||||
|
|
||||||
// Transition image to transfer format
|
// Transition image to transfer format
|
||||||
Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
|
Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
|
||||||
|
|
||||||
cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
|
cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||||
texture, vk::ImageLayout::eTransferDstOptimal,
|
texture, vk::ImageLayout::eTransferDstOptimal, region_count,
|
||||||
copy_region);
|
copy_regions.data());
|
||||||
|
|
||||||
// Prepare image for shader reads
|
// Prepare image for shader reads
|
||||||
Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
@ -301,7 +342,7 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
|
|||||||
|
|
||||||
void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> memory) {
|
void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> memory) {
|
||||||
u32 request_size = is_rgb ? (memory.size() / 3) * 4 :
|
u32 request_size = is_rgb ? (memory.size() / 3) * 4 :
|
||||||
(is_d24s8 ? (memory.size() / 4) * 5 : memory.size());
|
(is_d24s8 ? (memory.size() / 4) * 8 : memory.size());
|
||||||
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size);
|
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!");
|
LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!");
|
||||||
@ -312,25 +353,24 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
|
|||||||
|
|
||||||
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
|
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
|
||||||
|
|
||||||
// Copy pixels to staging buffer
|
std::array<vk::BufferImageCopy, 2> copy_regions;
|
||||||
vk::BufferImageCopy download_region{
|
u32 region_count = 1;
|
||||||
|
|
||||||
|
copy_regions[0] = vk::BufferImageCopy{
|
||||||
offset, row_length, region.extent.height,
|
offset, row_length, region.extent.height,
|
||||||
{aspect, level, layer, 1},
|
{aspect, level, layer, 1},
|
||||||
{region.offset.x, region.offset.y, 0},
|
{region.offset.x, region.offset.y, 0},
|
||||||
{region.extent.width, region.extent.height, 1}
|
{region.extent.width, region.extent.height, 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Automatically convert RGB to RGBA
|
if (aspect & vk::ImageAspectFlagBits::eDepth &&
|
||||||
if (is_rgb) {
|
aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
auto data = RGBAToRGB(memory);
|
// Copying both depth and stencil requires two seperate regions
|
||||||
std::memcpy(buffer, data.data(), data.size());
|
copy_regions[1] = copy_regions[0];
|
||||||
}
|
copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
||||||
else if (is_d24s8) {
|
copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
|
||||||
auto data = D32S8ToD24S8(memory);
|
|
||||||
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0]));
|
region_count++;
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::memcpy(buffer, memory.data(), memory.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition image to transfer format
|
// Transition image to transfer format
|
||||||
@ -338,16 +378,28 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
|
|||||||
Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal);
|
Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal);
|
||||||
|
|
||||||
cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
|
cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
|
||||||
g_vk_task_scheduler->GetStaging().GetBuffer(),
|
g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||||
download_region);
|
region_count, copy_regions.data());
|
||||||
|
|
||||||
|
// Restore layout
|
||||||
|
Transition(cmdbuffer, old_layout);
|
||||||
|
|
||||||
// Wait for the data to be available
|
// Wait for the data to be available
|
||||||
// NOTE: This is really slow and should be reworked
|
// NOTE: This is really slow and should be reworked
|
||||||
g_vk_task_scheduler->Submit(true);
|
g_vk_task_scheduler->Submit(true);
|
||||||
std::memcpy(memory.data(), buffer, memory.size_bytes());
|
|
||||||
|
|
||||||
// Restore layout
|
// Automatically convert RGB to RGBA
|
||||||
Transition(cmdbuffer, old_layout);
|
if (is_rgb) {
|
||||||
|
auto data = RGBAToRGB(std::span(buffer, request_size));
|
||||||
|
std::memcpy(memory.data(), data.data(), memory.size());
|
||||||
|
}
|
||||||
|
else if (is_d24s8) {
|
||||||
|
auto data = D32S8ToD24S8(std::span(buffer, request_size));
|
||||||
|
std::memcpy(memory.data(), data.data(), memory.size());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::memcpy(memory.data(), buffer, memory.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Out, typename In>
|
template <typename Out, typename In>
|
||||||
|
@ -32,8 +32,8 @@ public:
|
|||||||
~VKTexture();
|
~VKTexture();
|
||||||
|
|
||||||
/// Enable move operations
|
/// Enable move operations
|
||||||
VKTexture(VKTexture&&) = default;
|
VKTexture(VKTexture&& other) noexcept;
|
||||||
VKTexture& operator=(VKTexture&&) = default;
|
VKTexture& operator=(VKTexture&& other) noexcept;
|
||||||
|
|
||||||
/// Create a new Vulkan texture object
|
/// Create a new Vulkan texture object
|
||||||
void Create(const Info& info);
|
void Create(const Info& info);
|
||||||
|
Reference in New Issue
Block a user