renderer_vulkan: Fix texture allocation and destruction logic
This commit is contained in:
@ -56,10 +56,6 @@ protected:
|
||||
constexpr NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
// Enable std::move operations
|
||||
NonCopyable(NonCopyable&&) = default;
|
||||
NonCopyable& operator=(NonCopyable&&) = default;
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
|
@ -131,35 +131,6 @@ std::vector<const char*> RequiredExtensions(Frontend::WindowSystemType window_ty
|
||||
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)
|
||||
: RendererBase{window} {
|
||||
|
||||
@ -304,13 +275,13 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::
|
||||
|
||||
switch (screen.format) {
|
||||
case GPU::Regs::PixelFormat::RGBA8:
|
||||
texture_info.format = vk::Format::eR8G8B8A8Srgb;
|
||||
texture_info.format = vk::Format::eR8G8B8A8Unorm;
|
||||
break;
|
||||
|
||||
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.
|
||||
// The pixels will be converted automatically by Upload()
|
||||
texture_info.format = vk::Format::eR8G8B8Srgb;
|
||||
texture_info.format = vk::Format::eR8G8B8Unorm;
|
||||
break;
|
||||
|
||||
case GPU::Regs::PixelFormat::RGB565:
|
||||
@ -521,7 +492,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
// Set projection matrix
|
||||
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
|
||||
static_cast<float>(layout.height), 0.0f,
|
||||
-1.f, 1.f));
|
||||
0.f, 1.f));
|
||||
const bool stereo_single_screen = false
|
||||
/* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
|
||||
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& 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(),
|
||||
screen_infos[1].display_texture->GetView(),
|
||||
screen_infos[2].display_texture->GetView());
|
||||
@ -727,7 +698,7 @@ VideoCore::ResultStatus RendererVulkan::Init() {
|
||||
auto surface = CreateSurface(instance, render_window);
|
||||
g_vk_instace = std::make_unique<VKInstance>();
|
||||
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();
|
||||
|
||||
//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);
|
||||
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
|
||||
|
||||
buffer_memory = device.allocateMemory(alloc_info);
|
||||
device.bindBufferMemory(buffer, buffer_memory, 0);
|
||||
memory = device.allocateMemory(alloc_info);
|
||||
device.bindBufferMemory(buffer, memory, 0);
|
||||
|
||||
// Optionally map the buffer to CPU memory
|
||||
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) {
|
||||
@ -51,13 +51,16 @@ void VKBuffer::Recreate() {
|
||||
void VKBuffer::Destroy() {
|
||||
if (buffer) {
|
||||
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();
|
||||
device.destroyBuffer(buffer);
|
||||
device.freeMemory(buffer_memory);
|
||||
device.freeMemory(memory);
|
||||
|
||||
for (u32 i = 0; i < view_count; i++) {
|
||||
device.destroyBufferView(views[i]);
|
||||
|
@ -54,7 +54,7 @@ public:
|
||||
protected:
|
||||
Info buffer_info;
|
||||
vk::Buffer buffer;
|
||||
vk::DeviceMemory buffer_memory;
|
||||
vk::DeviceMemory memory;
|
||||
void* host_ptr = nullptr;
|
||||
std::array<vk::BufferView, MAX_BUFFER_VIEWS> views;
|
||||
u32 view_count{};
|
||||
|
@ -7,22 +7,21 @@
|
||||
#include "common/logging/log.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 {
|
||||
|
||||
std::unique_ptr<VKInstance> g_vk_instace;
|
||||
|
||||
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) {
|
||||
this->instance = instance;
|
||||
this->physical_device = physical_device;
|
||||
instance = new_instance;
|
||||
physical_device = gpu;
|
||||
|
||||
// Get physical device limits
|
||||
device_limits = physical_device.getProperties().limits;
|
||||
@ -98,15 +97,11 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) {
|
||||
}
|
||||
|
||||
// Create logical device
|
||||
device = physical_device.createDeviceUnique(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) */
|
||||
device = physical_device.createDevice(device_info);
|
||||
|
||||
// Grab the graphics and present queues.
|
||||
graphics_queue = device->getQueue(graphics_queue_family_index, 0);
|
||||
present_queue = device->getQueue(present_queue_family_index, 0);
|
||||
graphics_queue = device.getQueue(graphics_queue_family_index, 0);
|
||||
present_queue = device.getQueue(present_queue_family_index, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -195,7 +190,3 @@ bool VKInstance::FindExtensions() {
|
||||
}
|
||||
|
||||
} // 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,
|
||||
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::Instance GetInstance() const { return instance; }
|
||||
|
||||
/// Retrieve queue information
|
||||
u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; }
|
||||
@ -46,9 +47,9 @@ public:
|
||||
vk::Queue present_queue, graphics_queue;
|
||||
|
||||
// Core vulkan objects
|
||||
vk::Instance instance;
|
||||
vk::PhysicalDevice physical_device;
|
||||
vk::UniqueDevice device;
|
||||
vk::Instance instance;
|
||||
vk::Device device;
|
||||
|
||||
// Extensions and features
|
||||
std::vector<const char*> extensions;
|
||||
@ -67,7 +68,3 @@ public:
|
||||
extern std::unique_ptr<VKInstance> g_vk_instace;
|
||||
|
||||
} // 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);
|
||||
|
||||
// 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.SetVertexBuffer(vertex_buffer, 0);
|
||||
|
||||
@ -399,8 +400,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
@ -42,8 +42,8 @@ using SurfaceType = SurfaceParams::SurfaceType;
|
||||
using PixelFormat = SurfaceParams::PixelFormat;
|
||||
|
||||
static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
|
||||
vk::Format::eR8G8B8A8Srgb, // RGBA8
|
||||
vk::Format::eR8G8B8Srgb, // RGB8
|
||||
vk::Format::eR8G8B8A8Unorm, // RGBA8
|
||||
vk::Format::eR8G8B8Unorm, // RGB8
|
||||
vk::Format::eR5G5B5A1UnormPack16, // RGB5A1
|
||||
vk::Format::eR5G6B5UnormPack16, // RGB565
|
||||
vk::Format::eR4G4B4A4UnormPack16, // RGBA4
|
||||
@ -66,7 +66,7 @@ vk::Format GetFormatTuple(PixelFormat pixel_format) {
|
||||
ASSERT(tuple_idx < depth_format_tuples.size());
|
||||
return depth_format_tuples[tuple_idx];
|
||||
}
|
||||
return vk::Format::eR8G8B8A8Srgb;
|
||||
return vk::Format::eR8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
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) {
|
||||
// TODO: use vkCmdClearAttachments to clear subrects
|
||||
LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Render_Vulkan, "non-color fills not implemented");
|
||||
LOG_CRITICAL(Render_Vulkan, "Unsupported fill operation requested!");
|
||||
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
|
||||
VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Format format,
|
||||
void RasterizerCacheVulkan::AllocateTexture(VKTexture& target, SurfaceType type, vk::Format format,
|
||||
u32 width, u32 height) {
|
||||
// First check if the texture can be recycled
|
||||
auto recycled_tex = host_texture_recycler.find({format, width, height});
|
||||
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);
|
||||
return texture;
|
||||
return;
|
||||
}
|
||||
|
||||
auto GetUsage = [](SurfaceType type) {
|
||||
@ -378,7 +403,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
|
||||
|
||||
// Otherwise create a brand new texture
|
||||
u32 levels = std::log2(std::max(width, height)) + 1;
|
||||
VKTexture::Info texture_info = {
|
||||
VKTexture::Info texture_info{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.format = format,
|
||||
@ -388,13 +413,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo
|
||||
.levels = levels
|
||||
};
|
||||
|
||||
VKTexture texture;
|
||||
texture.Create(texture_info);
|
||||
|
||||
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() {
|
||||
@ -1431,8 +1454,8 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) {
|
||||
static_cast<SurfaceParams&>(*surface) = params;
|
||||
|
||||
surface->invalid_regions.insert(surface->GetInterval());
|
||||
surface->texture = AllocateSurfaceTexture(params.type, GetFormatTuple(surface->pixel_format),
|
||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
||||
AllocateTexture(surface->texture, params.type, GetFormatTuple(surface->pixel_format),
|
||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
@ -342,7 +342,7 @@ private:
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
public:
|
||||
VKTexture AllocateSurfaceTexture(SurfaceParams::SurfaceType type, vk::Format format,
|
||||
void AllocateTexture(VKTexture& target, SurfaceParams::SurfaceType type, vk::Format format,
|
||||
u32 width, u32 height);
|
||||
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.
|
||||
out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\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) {
|
||||
out += "depth /= gl_FragCoord.w;\n";
|
||||
}
|
||||
@ -1655,11 +1656,14 @@ void main() {
|
||||
texcoord0_w = vert_texcoord0_w;
|
||||
normquat = vert_normquat;
|
||||
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)
|
||||
//gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
|
||||
//gl_ClipDistance[1] = dot(clip_coef, vert_position);
|
||||
#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
|
||||
|
||||
// Convert OpenGL [-1, 1] to Vulkan [0, 1]
|
||||
vec4 position = vert_position;
|
||||
position.z = (position.z + 1) * 0.5;
|
||||
|
||||
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/renderer_vulkan.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_shader_gen.h"
|
||||
|
||||
@ -82,7 +83,7 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
|
||||
VKTexture::Info info{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.format = vk::Format::eR8G8B8A8Srgb,
|
||||
.format = vk::Format::eR8G8B8A8Unorm,
|
||||
.type = vk::ImageType::e2D,
|
||||
.view_type = vk::ImageViewType::e2D,
|
||||
.usage = vk::ImageUsageFlagBits::eSampled |
|
||||
@ -124,9 +125,12 @@ VulkanState::VulkanState(const std::shared_ptr<VKSwapChain>& swapchain) : swapch
|
||||
|
||||
VulkanState::~VulkanState() {
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
device.waitIdle();
|
||||
|
||||
// Destroy vertex shader
|
||||
device.destroyShaderModule(render_vertex_shader);
|
||||
device.destroyShaderModule(present_vertex_shader);
|
||||
device.destroyShaderModule(present_fragment_shader);
|
||||
|
||||
// Destroy pipeline layouts
|
||||
device.destroyPipelineLayout(render_pipeline_layout);
|
||||
@ -227,7 +231,7 @@ void VulkanState::UnbindTexture(u32 unit) {
|
||||
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::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear,
|
||||
vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op,
|
||||
@ -236,38 +240,37 @@ void VulkanState::BeginRendering(OptRef<VKTexture> color, OptRef<VKTexture> dept
|
||||
EndRendering();
|
||||
|
||||
// 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{};
|
||||
|
||||
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
|
||||
if (color.has_value()) {
|
||||
auto& image = color->get();
|
||||
image.Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
if (color != nullptr) {
|
||||
color->Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
|
||||
infos[0] = vk::RenderingAttachmentInfo{
|
||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
||||
color->GetView(), color->GetLayout(), {}, {}, {},
|
||||
color_load_op, color_store_op, color_clear
|
||||
};
|
||||
|
||||
render_info.colorAttachmentCount = 1;
|
||||
render_info.pColorAttachments = &infos[0];
|
||||
render_info.renderArea = color->GetArea();
|
||||
}
|
||||
|
||||
if (depth.has_value()) {
|
||||
auto& image = depth->get();
|
||||
image.Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
if (depth != nullptr) {
|
||||
depth->Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
infos[1] = vk::RenderingAttachmentInfo{
|
||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
||||
depth->GetView(), depth->GetLayout(), {}, {}, {},
|
||||
depth_load_op, depth_store_op, depth_clear
|
||||
};
|
||||
|
||||
render_info.pDepthAttachment = &infos[1];
|
||||
|
||||
|
||||
if (IsStencil(image.GetFormat())) {
|
||||
if (IsStencil(depth->GetFormat())) {
|
||||
infos[2] = vk::RenderingAttachmentInfo{
|
||||
image.GetView(), image.GetLayout(), {}, {}, {},
|
||||
depth->GetView(), depth->GetLayout(), {}, {}, {},
|
||||
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) {
|
||||
render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined;
|
||||
render_pipeline_key.depth_stencil = depth.has_value() ? depth->get().GetFormat() : vk::Format::eUndefined;
|
||||
render_pipeline_key.color = color != nullptr ?
|
||||
color->GetFormat() :
|
||||
vk::Format::eUndefined;
|
||||
render_pipeline_key.depth_stencil = depth != nullptr ?
|
||||
depth->GetFormat() :
|
||||
vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
// Begin rendering
|
||||
|
@ -17,9 +17,6 @@ namespace Vulkan {
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET_COUNT = 4;
|
||||
|
||||
template <typename T>
|
||||
using OptRef = std::optional<std::reference_wrapper<T>>;
|
||||
|
||||
struct DrawInfo {
|
||||
glm::mat4 modelview;
|
||||
glm::vec4 i_resolution;
|
||||
@ -93,7 +90,7 @@ public:
|
||||
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
|
||||
|
||||
/// 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::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad,
|
||||
vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore,
|
||||
|
@ -19,9 +19,13 @@ VKSwapChain::VKSwapChain(vk::SurfaceKHR surface_) : surface(surface_) {
|
||||
|
||||
VKSwapChain::~VKSwapChain() {
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
auto instance = g_vk_instace->GetInstance();
|
||||
device.waitIdle();
|
||||
|
||||
device.destroySemaphore(render_finished);
|
||||
device.destroySemaphore(image_available);
|
||||
device.destroySwapchainKHR(swapchain);
|
||||
instance.destroySurfaceKHR(surface);
|
||||
}
|
||||
|
||||
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
|
||||
if (!image_available) {
|
||||
image_available = device.createSemaphoreUnique({});
|
||||
image_available = device.createSemaphore({});
|
||||
}
|
||||
|
||||
if (!render_finished) {
|
||||
render_finished = device.createSemaphoreUnique({});
|
||||
render_finished = device.createSemaphore({});
|
||||
}
|
||||
|
||||
// Create framebuffer and image views
|
||||
@ -78,7 +82,7 @@ constexpr u64 ACQUIRE_TIMEOUT = 1000000000;
|
||||
|
||||
void VKSwapChain::AcquireNextImage() {
|
||||
auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT,
|
||||
image_available.get(), VK_NULL_HANDLE,
|
||||
image_available, VK_NULL_HANDLE,
|
||||
&image_index);
|
||||
switch (result) {
|
||||
case vk::Result::eSuccess:
|
||||
@ -98,7 +102,7 @@ void VKSwapChain::AcquireNextImage() {
|
||||
void VKSwapChain::Present() {
|
||||
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);
|
||||
|
||||
switch (result) {
|
||||
|
@ -42,8 +42,8 @@ public:
|
||||
vk::SurfaceKHR GetSurface() const { return surface; }
|
||||
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
|
||||
vk::SwapchainKHR GetSwapChain() const { return swapchain; }
|
||||
const vk::Semaphore& GetAvailableSemaphore() const { return image_available.get(); }
|
||||
const vk::Semaphore& GetRenderSemaphore() const { return render_finished.get(); }
|
||||
const vk::Semaphore& GetAvailableSemaphore() const { return image_available; }
|
||||
const vk::Semaphore& GetRenderSemaphore() const { return render_finished; }
|
||||
VKTexture& GetCurrentImage() { return swapchain_images[image_index]; }
|
||||
|
||||
private:
|
||||
@ -53,7 +53,7 @@ private:
|
||||
private:
|
||||
SwapChainDetails details{};
|
||||
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};
|
||||
|
||||
vk::SwapchainKHR swapchain{VK_NULL_HANDLE};
|
||||
|
@ -11,10 +11,11 @@
|
||||
namespace Vulkan {
|
||||
|
||||
VKTaskScheduler::~VKTaskScheduler() {
|
||||
SyncToGPU();
|
||||
|
||||
// Destroy Vulkan resources
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
device.waitIdle();
|
||||
|
||||
SyncToGPU();
|
||||
device.destroyCommandPool(command_pool);
|
||||
device.destroySemaphore(timeline);
|
||||
|
||||
@ -63,7 +64,8 @@ bool VKTaskScheduler::Create() {
|
||||
.size = STAGING_BUFFER_SIZE,
|
||||
.properties = vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
.usage = vk::BufferUsageFlagBits::eTransferSrc
|
||||
.usage = vk::BufferUsageFlagBits::eTransferSrc |
|
||||
vk::BufferUsageFlagBits::eTransferDst
|
||||
};
|
||||
|
||||
// Should be enough for a single frame
|
||||
@ -131,6 +133,7 @@ void VKTaskScheduler::SyncToGPU(u64 task_index) {
|
||||
for (auto& func : task.cleanups) {
|
||||
func();
|
||||
}
|
||||
task.cleanups.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ static int BytesPerPixel(vk::Format format) {
|
||||
case vk::Format::eD32Sfloat:
|
||||
case vk::Format::eB8G8R8A8Unorm:
|
||||
case vk::Format::eR8G8B8A8Uint:
|
||||
case vk::Format::eR8G8B8A8Srgb:
|
||||
case vk::Format::eR8G8B8A8Unorm:
|
||||
case vk::Format::eD24UnormS8Uint:
|
||||
return 4;
|
||||
case vk::Format::eR8G8B8Uint:
|
||||
case vk::Format::eR8G8B8Unorm:
|
||||
case vk::Format::eR8G8B8Srgb:
|
||||
return 3;
|
||||
case vk::Format::eR5G6B5UnormPack16:
|
||||
@ -58,15 +58,41 @@ VKTexture::~VKTexture() {
|
||||
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) {
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
info = create_info;
|
||||
|
||||
// Emulate RGB8 format with RGBA8
|
||||
is_rgb = false;
|
||||
if (info.format == vk::Format::eR8G8B8Srgb) {
|
||||
if (info.format == vk::Format::eR8G8B8Unorm) {
|
||||
is_rgb = true;
|
||||
info.format = vk::Format::eR8G8B8A8Srgb;
|
||||
info.format = vk::Format::eR8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
is_d24s8 = false;
|
||||
@ -136,7 +162,9 @@ void VKTexture::Destroy() {
|
||||
auto& state = VulkanState::Get();
|
||||
state.UnbindTexture(*this);
|
||||
|
||||
auto deleter = [this]() {
|
||||
auto deleter = [texture = texture,
|
||||
view = view,
|
||||
memory = memory]() {
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
if (texture) {
|
||||
std::cout << "Surface destroyed!\n";
|
||||
@ -154,7 +182,7 @@ void VKTexture::Destroy() {
|
||||
// If the image was adopted (probably from the swapchain) then only
|
||||
// destroy the view
|
||||
if (adopted) {
|
||||
g_vk_task_scheduler->Schedule([this](){
|
||||
g_vk_task_scheduler->Schedule([view = view](){
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
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());
|
||||
}
|
||||
|
||||
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,
|
||||
{aspect, level, layer, 1},
|
||||
{region.offset.x, region.offset.y, 0},
|
||||
{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(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
|
||||
|
||||
cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||
texture, vk::ImageLayout::eTransferDstOptimal,
|
||||
copy_region);
|
||||
texture, vk::ImageLayout::eTransferDstOptimal, region_count,
|
||||
copy_regions.data());
|
||||
|
||||
// Prepare image for shader reads
|
||||
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) {
|
||||
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);
|
||||
if (!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();
|
||||
|
||||
// Copy pixels to staging buffer
|
||||
vk::BufferImageCopy download_region{
|
||||
std::array<vk::BufferImageCopy, 2> copy_regions;
|
||||
u32 region_count = 1;
|
||||
|
||||
copy_regions[0] = vk::BufferImageCopy{
|
||||
offset, row_length, region.extent.height,
|
||||
{aspect, level, layer, 1},
|
||||
{region.offset.x, region.offset.y, 0},
|
||||
{region.extent.width, region.extent.height, 1}
|
||||
};
|
||||
|
||||
// Automatically convert RGB to RGBA
|
||||
if (is_rgb) {
|
||||
auto data = RGBAToRGB(memory);
|
||||
std::memcpy(buffer, data.data(), data.size());
|
||||
}
|
||||
else if (is_d24s8) {
|
||||
auto data = D32S8ToD24S8(memory);
|
||||
std::memcpy(buffer, data.data(), data.size() * sizeof(data[0]));
|
||||
}
|
||||
else {
|
||||
std::memcpy(buffer, memory.data(), memory.size());
|
||||
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
|
||||
@ -338,16 +378,28 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region
|
||||
Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal);
|
||||
|
||||
cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
|
||||
g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||
download_region);
|
||||
g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||
region_count, copy_regions.data());
|
||||
|
||||
// Restore layout
|
||||
Transition(cmdbuffer, old_layout);
|
||||
|
||||
// Wait for the data to be available
|
||||
// NOTE: This is really slow and should be reworked
|
||||
g_vk_task_scheduler->Submit(true);
|
||||
std::memcpy(memory.data(), buffer, memory.size_bytes());
|
||||
|
||||
// Restore layout
|
||||
Transition(cmdbuffer, old_layout);
|
||||
// Automatically convert RGB to RGBA
|
||||
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>
|
||||
|
@ -32,8 +32,8 @@ public:
|
||||
~VKTexture();
|
||||
|
||||
/// Enable move operations
|
||||
VKTexture(VKTexture&&) = default;
|
||||
VKTexture& operator=(VKTexture&&) = default;
|
||||
VKTexture(VKTexture&& other) noexcept;
|
||||
VKTexture& operator=(VKTexture&& other) noexcept;
|
||||
|
||||
/// Create a new Vulkan texture object
|
||||
void Create(const Info& info);
|
||||
|
Reference in New Issue
Block a user