mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
render to texture
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#include "Emulator/Graphics/Objects/DepthStencilBuffer.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* DepthStencilBufferObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
|
||||
VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::Create");
|
||||
|
||||
EXIT_IF(size == nullptr || vaddr == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto pixel_format = static_cast<VkFormat>(params[PARAM_FORMAT]);
|
||||
auto width = params[PARAM_WIDTH];
|
||||
auto height = params[PARAM_HEIGHT];
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(pixel_format == VK_FORMAT_UNDEFINED);
|
||||
EXIT_NOT_IMPLEMENTED(width == 0);
|
||||
EXIT_NOT_IMPLEMENTED(height == 0);
|
||||
|
||||
auto* vk_obj = new DepthStencilVulkanImage;
|
||||
|
||||
vk_obj->extent.width = width;
|
||||
vk_obj->extent.height = height;
|
||||
vk_obj->format = pixel_format;
|
||||
vk_obj->image = nullptr;
|
||||
vk_obj->image_view = nullptr;
|
||||
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImageCreateInfo image_info {};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.pNext = nullptr;
|
||||
image_info.flags = 0;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = vk_obj->extent.width;
|
||||
image_info.extent.height = vk_obj->extent.height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = vk_obj->format;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = vk_obj->layout;
|
||||
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
|
||||
|
||||
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
|
||||
|
||||
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
bool allocated = VulkanAllocate(ctx, mem);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!allocated);
|
||||
|
||||
VulkanBindImageMemory(ctx, vk_obj, mem);
|
||||
|
||||
vk_obj->memory = *mem;
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
|
||||
|
||||
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
|
||||
|
||||
VkImageViewCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.image = vk_obj->image;
|
||||
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
create_info.format = vk_obj->format;
|
||||
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
create_info.subresourceRange.baseArrayLayer = 0;
|
||||
create_info.subresourceRange.baseMipLevel = 0;
|
||||
create_info.subresourceRange.layerCount = 1;
|
||||
create_info.subresourceRange.levelCount = 1;
|
||||
|
||||
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
|
||||
|
||||
UtilSetDepthLayoutOptimal(vk_obj);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
|
||||
const uint64_t* /*size*/, int /*vaddr_num*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::update_func");
|
||||
}
|
||||
|
||||
bool DepthStencilBufferObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_FORMAT] == other[PARAM_FORMAT] && params[PARAM_WIDTH] == other[PARAM_WIDTH] &&
|
||||
params[PARAM_HEIGHT] == other[PARAM_HEIGHT] && params[PARAM_HTILE] == other[PARAM_HTILE]);
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<DepthStencilVulkanImage*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
DeleteFramebuffer(vk_obj);
|
||||
|
||||
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
|
||||
|
||||
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
|
||||
|
||||
VulkanFree(ctx, mem);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t DepthStencilBufferObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t DepthStencilBufferObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
#include "Emulator/Graphics/Objects/IndexBuffer.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* IndexBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
|
||||
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto* vk_obj = new VulkanBuffer;
|
||||
|
||||
vk_obj->usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
||||
vk_obj->memory.property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
vk_obj->buffer = nullptr;
|
||||
|
||||
VulkanCreateBuffer(ctx, *size, vk_obj);
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
|
||||
|
||||
VulkanBuffer staging_buffer {};
|
||||
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
VulkanCreateBuffer(ctx, *size, &staging_buffer);
|
||||
EXIT_NOT_IMPLEMENTED(staging_buffer.buffer == nullptr);
|
||||
|
||||
void* data = nullptr;
|
||||
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, *size, 0, &data);
|
||||
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
|
||||
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
|
||||
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
|
||||
VulkanUnmapMemory(ctx, &staging_buffer.memory);
|
||||
|
||||
UtilCopyBuffer(&staging_buffer, vk_obj, *size);
|
||||
|
||||
VulkanDeleteBuffer(ctx, &staging_buffer);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
|
||||
const uint64_t* /*size*/, int /*vaddr_num*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::update_func");
|
||||
|
||||
KYTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
bool IndexBufferGpuObject::Equal(const uint64_t* /*other*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(vk_obj->buffer == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
VulkanDeleteBuffer(ctx, vk_obj);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t IndexBufferGpuObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t IndexBufferGpuObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,395 @@
|
||||
#include "Emulator/Graphics/Objects/Label.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
enum LabelStatus
|
||||
{
|
||||
New,
|
||||
Active,
|
||||
ActiveDeleted,
|
||||
NotActive,
|
||||
};
|
||||
|
||||
struct LabelCallbacks
|
||||
{
|
||||
uint64_t* dst_gpu_addr64 = nullptr;
|
||||
uint64_t value64 = 0;
|
||||
uint32_t* dst_gpu_addr32 = nullptr;
|
||||
uint32_t value32 = 0;
|
||||
LabelGpuObject::callback_t callback_1 = nullptr;
|
||||
LabelGpuObject::callback_t callback_2 = nullptr;
|
||||
uint64_t args[4] = {};
|
||||
};
|
||||
|
||||
struct Label
|
||||
{
|
||||
VkDevice device = nullptr;
|
||||
VkEvent event = nullptr;
|
||||
LabelStatus status = LabelStatus::New;
|
||||
LabelCallbacks callbacks;
|
||||
CommandBuffer* buffer = nullptr;
|
||||
};
|
||||
|
||||
class LabelManager
|
||||
{
|
||||
public:
|
||||
LabelManager()
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
|
||||
Core::Thread t(ThreadRun, this);
|
||||
t.Detach();
|
||||
}
|
||||
virtual ~LabelManager() { KYTY_NOT_IMPLEMENTED; }
|
||||
KYTY_CLASS_NO_COPY(LabelManager);
|
||||
|
||||
Label* Create(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args);
|
||||
Label* Create(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args);
|
||||
void Delete(Label* label);
|
||||
void Set(CommandBuffer* buffer, Label* label);
|
||||
|
||||
private:
|
||||
static void ThreadRun(void* data);
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
Core::CondVar m_cond_var;
|
||||
Vector<Label*> m_labels;
|
||||
};
|
||||
|
||||
static LabelManager* g_label_manager = nullptr;
|
||||
|
||||
void LabelManager::ThreadRun(void* data)
|
||||
{
|
||||
auto* manager = static_cast<LabelManager*>(data);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
manager->m_mutex.Lock();
|
||||
|
||||
int active_count = 0;
|
||||
|
||||
Vector<Label*> deleted_labels;
|
||||
Vector<LabelCallbacks> fired_labels;
|
||||
|
||||
for (auto& label: manager->m_labels)
|
||||
{
|
||||
if (label->status == LabelStatus::Active || label->status == LabelStatus::ActiveDeleted)
|
||||
{
|
||||
active_count++;
|
||||
|
||||
if (vkGetEventStatus(label->device, label->event) == VK_EVENT_SET)
|
||||
{
|
||||
if (label->status == LabelStatus::ActiveDeleted)
|
||||
{
|
||||
deleted_labels.Add(label);
|
||||
}
|
||||
|
||||
label->status = LabelStatus::NotActive;
|
||||
|
||||
fired_labels.Add(label->callbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& label: deleted_labels)
|
||||
{
|
||||
manager->Delete(label);
|
||||
}
|
||||
|
||||
if (active_count == 0)
|
||||
{
|
||||
manager->m_cond_var.Wait(&manager->m_mutex);
|
||||
}
|
||||
|
||||
manager->m_mutex.Unlock();
|
||||
|
||||
for (auto& label: fired_labels)
|
||||
{
|
||||
bool write = true;
|
||||
|
||||
if (label.callback_1 != nullptr)
|
||||
{
|
||||
write = label.callback_1(label.args);
|
||||
}
|
||||
|
||||
if (write && label.dst_gpu_addr64 != nullptr)
|
||||
{
|
||||
*label.dst_gpu_addr64 = label.value64;
|
||||
|
||||
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%016" PRIx64 "\n" FG_DEFAULT,
|
||||
reinterpret_cast<uint64_t>(label.dst_gpu_addr64), label.value64);
|
||||
}
|
||||
|
||||
if (write && label.dst_gpu_addr32 != nullptr)
|
||||
{
|
||||
*label.dst_gpu_addr32 = label.value32;
|
||||
|
||||
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%08" PRIx32 "\n" FG_DEFAULT,
|
||||
reinterpret_cast<uint64_t>(label.dst_gpu_addr32), label.value32);
|
||||
}
|
||||
|
||||
if (label.callback_2 != nullptr)
|
||||
{
|
||||
label.callback_2(label.args);
|
||||
}
|
||||
}
|
||||
|
||||
Core::Thread::SleepMicro(100);
|
||||
}
|
||||
}
|
||||
|
||||
Label* LabelManager::Create(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(dst_gpu_addr == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto* label = new Label;
|
||||
|
||||
label->status = LabelStatus::New;
|
||||
label->callbacks.dst_gpu_addr64 = dst_gpu_addr;
|
||||
label->callbacks.value64 = value;
|
||||
label->callbacks.dst_gpu_addr32 = nullptr;
|
||||
label->callbacks.value32 = 0;
|
||||
label->event = nullptr;
|
||||
label->device = ctx->device;
|
||||
label->callbacks.callback_1 = callback_1;
|
||||
label->callbacks.callback_2 = callback_2;
|
||||
label->callbacks.args[0] = args[0];
|
||||
label->callbacks.args[1] = args[1];
|
||||
label->callbacks.args[2] = args[2];
|
||||
label->callbacks.args[3] = args[3];
|
||||
label->buffer = nullptr;
|
||||
|
||||
VkEventCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
|
||||
vkCreateEvent(ctx->device, &create_info, nullptr, &label->event);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label->event == nullptr);
|
||||
|
||||
m_labels.Add(label);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
Label* LabelManager::Create(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(dst_gpu_addr == nullptr);
|
||||
EXIT_IF(args == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto* label = new Label;
|
||||
|
||||
label->status = LabelStatus::New;
|
||||
label->callbacks.dst_gpu_addr32 = dst_gpu_addr;
|
||||
label->callbacks.value32 = value;
|
||||
label->callbacks.dst_gpu_addr64 = nullptr;
|
||||
label->callbacks.value64 = 0;
|
||||
label->event = nullptr;
|
||||
label->device = ctx->device;
|
||||
label->callbacks.callback_1 = callback_1;
|
||||
label->callbacks.callback_2 = callback_2;
|
||||
label->callbacks.args[0] = args[0];
|
||||
label->callbacks.args[1] = args[1];
|
||||
label->callbacks.args[2] = args[2];
|
||||
label->callbacks.args[3] = args[3];
|
||||
|
||||
VkEventCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
|
||||
vkCreateEvent(ctx->device, &create_info, nullptr, &label->event);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label->event == nullptr);
|
||||
|
||||
m_labels.Add(label);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
void LabelManager::Delete(Label* label)
|
||||
{
|
||||
EXIT_IF(label == nullptr);
|
||||
EXIT_IF(label->event == nullptr);
|
||||
EXIT_IF(label->device == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto index = m_labels.Find(label);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_labels.IndexValid(index));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label->status != LabelStatus::NotActive && label->status != LabelStatus::Active);
|
||||
|
||||
if (label->status == LabelStatus::Active)
|
||||
{
|
||||
label->status = LabelStatus::ActiveDeleted;
|
||||
} else
|
||||
{
|
||||
m_labels.RemoveAt(index);
|
||||
|
||||
EXIT_IF(label->buffer == nullptr);
|
||||
|
||||
// All submitted commands that refer to event must have completed execution
|
||||
label->buffer->CommandProcessorWait();
|
||||
|
||||
vkDestroyEvent(label->device, label->event, nullptr);
|
||||
|
||||
delete label;
|
||||
}
|
||||
}
|
||||
|
||||
void LabelManager::Set(CommandBuffer* buffer, Label* label)
|
||||
{
|
||||
EXIT_IF(label == nullptr);
|
||||
EXIT_IF(buffer == nullptr);
|
||||
EXIT_IF(buffer->IsInvalid());
|
||||
EXIT_IF(label->event == nullptr);
|
||||
EXIT_IF(label->device == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto index = m_labels.Find(label);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_labels.IndexValid(index));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label->status != LabelStatus::New && label->status != LabelStatus::NotActive);
|
||||
|
||||
label->status = LabelStatus::Active;
|
||||
|
||||
EXIT_IF(label->event == nullptr);
|
||||
|
||||
label->buffer = buffer;
|
||||
|
||||
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_buffer == nullptr);
|
||||
|
||||
vkResetEvent(label->device, label->event);
|
||||
vkCmdSetEvent(vk_buffer, label->event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
|
||||
|
||||
m_cond_var.Signal();
|
||||
}
|
||||
|
||||
void LabelInit()
|
||||
{
|
||||
EXIT_IF(g_label_manager != nullptr);
|
||||
|
||||
g_label_manager = new LabelManager;
|
||||
}
|
||||
|
||||
Label* LabelCreate(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args)
|
||||
{
|
||||
EXIT_IF(g_label_manager == nullptr);
|
||||
|
||||
return g_label_manager->Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
|
||||
}
|
||||
|
||||
Label* LabelCreate(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
|
||||
LabelGpuObject::callback_t callback_2, const uint64_t* args)
|
||||
{
|
||||
EXIT_IF(g_label_manager == nullptr);
|
||||
|
||||
return g_label_manager->Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
|
||||
}
|
||||
|
||||
void LabelDelete(Label* label)
|
||||
{
|
||||
EXIT_IF(g_label_manager == nullptr);
|
||||
|
||||
g_label_manager->Delete(label);
|
||||
}
|
||||
|
||||
void LabelSet(CommandBuffer* buffer, Label* label)
|
||||
{
|
||||
EXIT_IF(g_label_manager == nullptr);
|
||||
|
||||
g_label_manager->Set(buffer, label);
|
||||
}
|
||||
|
||||
void* LabelGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* /*mem*/) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("LabelGpuObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(*size != 8 && *size != 4);
|
||||
|
||||
auto value = params[PARAM_VALUE];
|
||||
auto callback_1 = reinterpret_cast<LabelGpuObject::callback_t>(params[PARAM_CALLBACK_1]);
|
||||
auto callback_2 = reinterpret_cast<LabelGpuObject::callback_t>(params[PARAM_CALLBACK_2]);
|
||||
|
||||
auto* label_obj =
|
||||
(*size == 8 ? LabelCreate(ctx, reinterpret_cast<uint64_t*>(*vaddr), value, callback_1, callback_2, params + PARAM_ARG_1)
|
||||
: (*size == 4 ? LabelCreate(ctx, reinterpret_cast<uint32_t*>(*vaddr), static_cast<uint32_t>(value), callback_1,
|
||||
callback_2, params + PARAM_ARG_1)
|
||||
: nullptr));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label_obj == nullptr);
|
||||
|
||||
return label_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
|
||||
const uint64_t* /*size*/, int /*vaddr_num*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("LabelGpuObject::update_func");
|
||||
|
||||
KYTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
bool LabelGpuObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_VALUE] == other[PARAM_VALUE] && params[PARAM_CALLBACK_1] == other[PARAM_CALLBACK_1] &&
|
||||
params[PARAM_CALLBACK_2] == other[PARAM_CALLBACK_2] && params[PARAM_ARG_1] == other[PARAM_ARG_1] &&
|
||||
params[PARAM_ARG_2] == other[PARAM_ARG_2] && params[PARAM_ARG_3] == other[PARAM_ARG_3] &&
|
||||
params[PARAM_ARG_4] == other[PARAM_ARG_4]);
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* /*ctx*/, void* obj, VulkanMemory* /*mem*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("LabelGpuObject::delete_func");
|
||||
|
||||
auto* label_obj = reinterpret_cast<Label*>(obj);
|
||||
|
||||
EXIT_IF(label_obj == nullptr);
|
||||
|
||||
LabelDelete(label_obj);
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t LabelGpuObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t LabelGpuObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,211 @@
|
||||
#include "Emulator/Graphics/Objects/RenderTexture.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
//#include "Emulator/Graphics/Tile.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* RenderTextureObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("RenderTextureObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto pixel_format = params[PARAM_FORMAT];
|
||||
auto width = params[PARAM_WIDTH];
|
||||
auto height = params[PARAM_HEIGHT];
|
||||
|
||||
VkFormat vk_format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
switch (pixel_format) // NOLINT
|
||||
{
|
||||
case static_cast<uint64_t>(RenderTextureFormat::R8G8B8A8Unorm): vk_format = VK_FORMAT_R8G8B8A8_UNORM; break;
|
||||
default: EXIT("unknown format: %" PRIu64 "\n", pixel_format);
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(width == 0);
|
||||
EXIT_NOT_IMPLEMENTED(height == 0);
|
||||
|
||||
auto* vk_obj = new RenderTextureVulkanImage;
|
||||
|
||||
vk_obj->extent.width = width;
|
||||
vk_obj->extent.height = height;
|
||||
vk_obj->format = vk_format;
|
||||
vk_obj->image = nullptr;
|
||||
vk_obj->image_view = nullptr;
|
||||
|
||||
VkImageCreateInfo image_info {};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.pNext = nullptr;
|
||||
image_info.flags = 0;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = vk_obj->extent.width;
|
||||
image_info.extent.height = vk_obj->extent.height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = vk_obj->format;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) |
|
||||
static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_TRANSFER_SRC_BIT) |
|
||||
static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_TRANSFER_DST_BIT) |
|
||||
static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
|
||||
|
||||
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
|
||||
|
||||
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
bool allocated = VulkanAllocate(ctx, mem);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!allocated);
|
||||
|
||||
VulkanBindImageMemory(ctx, vk_obj, mem);
|
||||
|
||||
vk_obj->memory = *mem;
|
||||
|
||||
printf("RenderTextureObject::Create()\n");
|
||||
printf("\t mem->requirements.size = %" PRIu64 "\n", mem->requirements.size);
|
||||
printf("\t width = %" PRIu64 "\n", width);
|
||||
printf("\t height = %" PRIu64 "\n", height);
|
||||
printf("\t size = %" PRIu64 "\n", *size);
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
|
||||
|
||||
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
|
||||
|
||||
VkImageViewCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.image = vk_obj->image;
|
||||
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
create_info.format = vk_obj->format;
|
||||
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
create_info.subresourceRange.baseArrayLayer = 0;
|
||||
create_info.subresourceRange.baseMipLevel = 0;
|
||||
create_info.subresourceRange.layerCount = 1;
|
||||
create_info.subresourceRange.levelCount = 1;
|
||||
|
||||
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static bool buffer_is_tiled(uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
if ((size & 0x7u) == 0)
|
||||
{
|
||||
const auto* ptr = reinterpret_cast<const uint64_t*>(vaddr);
|
||||
const auto* ptr_end = reinterpret_cast<const uint64_t*>(vaddr + size / 8);
|
||||
for (uint64_t element = *ptr; ptr < ptr_end; ptr++)
|
||||
{
|
||||
if (element != *ptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("RenderTextureObject::update_func");
|
||||
|
||||
EXIT_IF(obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(params == nullptr);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
|
||||
|
||||
auto* vk_obj = static_cast<RenderTextureVulkanImage*>(obj);
|
||||
|
||||
bool tiled = (params[RenderTextureObject::PARAM_TILED] != 0);
|
||||
// bool neo = (params[RenderTextureObject::PARAM_NEO] != 0);
|
||||
auto pitch = params[RenderTextureObject::PARAM_PITCH];
|
||||
auto width = params[RenderTextureObject::PARAM_WIDTH];
|
||||
// auto height = params[RenderTextureObject::PARAM_HEIGHT];
|
||||
|
||||
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
if (tiled && buffer_is_tiled(*vaddr, *size))
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(width != pitch);
|
||||
auto* temp_buf = new uint8_t[*size];
|
||||
KYTY_NOT_IMPLEMENTED;
|
||||
// TODO()
|
||||
// TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::VideoOutTiled, width, height, neo);
|
||||
// UtilFillImage(ctx, vk_obj, temp_buf, *size, pitch);
|
||||
delete[] temp_buf;
|
||||
} else
|
||||
{
|
||||
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderTextureObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_FORMAT] == other[PARAM_FORMAT] && params[PARAM_WIDTH] == other[PARAM_WIDTH] &&
|
||||
params[PARAM_HEIGHT] == other[PARAM_HEIGHT] && params[PARAM_TILED] == other[PARAM_TILED] &&
|
||||
params[PARAM_PITCH] == other[PARAM_PITCH] && params[PARAM_PITCH] == other[PARAM_PITCH]);
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("RenderTextureObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<RenderTextureVulkanImage*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
DeleteDescriptor(vk_obj);
|
||||
|
||||
DeleteFramebuffer(vk_obj);
|
||||
|
||||
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
|
||||
|
||||
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
|
||||
|
||||
VulkanFree(ctx, mem);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t RenderTextureObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t RenderTextureObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "Emulator/Graphics/Objects/StorageBuffer.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* StorageBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
|
||||
VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
|
||||
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto* vk_obj = new VulkanBuffer;
|
||||
|
||||
vk_obj->usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
vk_obj->memory.property = static_cast<uint32_t>(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||
vk_obj->buffer = nullptr;
|
||||
|
||||
VulkanCreateBuffer(ctx, *size, vk_obj);
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
|
||||
|
||||
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* ctx, const uint64_t* /*params*/, void* obj, const uint64_t* vaddr, const uint64_t* size,
|
||||
int vaddr_num)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::update_func");
|
||||
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(obj == nullptr);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
|
||||
|
||||
void* data = nullptr;
|
||||
// vkMapMemory(ctx->device, vk_obj->memory.memory, vk_obj->memory.offset, *size, 0, &data);
|
||||
VulkanMapMemory(ctx, &vk_obj->memory, &data);
|
||||
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
|
||||
// vkUnmapMemory(ctx->device, vk_obj->memory.memory);
|
||||
VulkanUnmapMemory(ctx, &vk_obj->memory);
|
||||
}
|
||||
|
||||
bool StorageBufferGpuObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return params[0] == other[0] && params[1] == other[1];
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(vk_obj->buffer == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
VulkanDeleteBuffer(ctx, vk_obj);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
static void write_back(GraphicContext* ctx, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back");
|
||||
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(obj == nullptr);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
|
||||
|
||||
void* data = nullptr;
|
||||
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::vkMapMemory");
|
||||
// vkMapMemory(ctx->device, vk_obj->memory.memory, vk_obj->memory.offset, *size, 0, &data);
|
||||
VulkanMapMemory(ctx, &vk_obj->memory, &data);
|
||||
KYTY_PROFILER_END_BLOCK;
|
||||
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::memcpy");
|
||||
memcpy(reinterpret_cast<void*>(*vaddr), data, *size);
|
||||
KYTY_PROFILER_END_BLOCK;
|
||||
|
||||
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::vkUnmapMemory");
|
||||
// vkUnmapMemory(ctx->device, vk_obj->memory.memory);
|
||||
VulkanUnmapMemory(ctx, &vk_obj->memory);
|
||||
KYTY_PROFILER_END_BLOCK;
|
||||
}
|
||||
|
||||
GpuObject::write_back_func_t StorageBufferGpuObject::GetWriteBackFunc() const
|
||||
{
|
||||
return write_back;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t StorageBufferGpuObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t StorageBufferGpuObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,344 @@
|
||||
#include "Emulator/Graphics/Objects/Texture.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Config.h"
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Graphics/Tile.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
static VkFormat get_texture_format(uint32_t dfmt, uint32_t nfmt)
|
||||
{
|
||||
if (nfmt == 9 && dfmt == 10)
|
||||
{
|
||||
return VK_FORMAT_R8G8B8A8_SRGB;
|
||||
}
|
||||
if (nfmt == 9 && dfmt == 37)
|
||||
{
|
||||
return VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
}
|
||||
EXIT("unknown format: nfmt = %u, dfmt = %u\n", nfmt, dfmt);
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
static VkComponentSwizzle get_swizzle(uint8_t s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case 0: return VK_COMPONENT_SWIZZLE_ZERO; break;
|
||||
case 1: return VK_COMPONENT_SWIZZLE_ONE; break;
|
||||
case 4: return VK_COMPONENT_SWIZZLE_R; break;
|
||||
case 5: return VK_COMPONENT_SWIZZLE_G; break;
|
||||
case 6: return VK_COMPONENT_SWIZZLE_B; break;
|
||||
case 7: return VK_COMPONENT_SWIZZLE_A; break;
|
||||
case 2:
|
||||
case 3:
|
||||
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
|
||||
}
|
||||
return VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
}
|
||||
|
||||
static bool CheckFormat(GraphicContext* ctx, VkImageCreateInfo* image_info)
|
||||
{
|
||||
VkImageFormatProperties props {};
|
||||
if (vkGetPhysicalDeviceImageFormatProperties(ctx->physical_device, image_info->format, image_info->imageType, image_info->tiling,
|
||||
image_info->usage, image_info->flags, &props) == VK_ERROR_FORMAT_NOT_SUPPORTED)
|
||||
{
|
||||
if (image_info->format == VK_FORMAT_R8G8B8A8_SRGB)
|
||||
{
|
||||
// TODO() convert SRGB -> LINEAR in shader
|
||||
image_info->format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
bool result = CheckFormat(ctx, image_info);
|
||||
printf("replace VK_FORMAT_R8G8B8A8_SRGB => VK_FORMAT_R8G8B8A8_UNORM [%s]\n", (!result ? "FAIL" : "SUCCESS"));
|
||||
return result;
|
||||
}
|
||||
if (image_info->format == VK_FORMAT_B8G8R8A8_SRGB)
|
||||
{
|
||||
// TODO() convert SRGB -> LINEAR in shader
|
||||
image_info->format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
bool result = CheckFormat(ctx, image_info);
|
||||
printf("replace VK_FORMAT_B8G8R8A8_SRGB => VK_FORMAT_B8G8R8A8_UNORM [%s]\n", (!result ? "FAIL" : "SUCCESS"));
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CheckSwizzle(GraphicContext* /*ctx*/, VkImageCreateInfo* image_info, VkComponentMapping* components)
|
||||
{
|
||||
if ((image_info->usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0)
|
||||
{
|
||||
if (components->r == VK_COMPONENT_SWIZZLE_R && components->g == VK_COMPONENT_SWIZZLE_G && components->b == VK_COMPONENT_SWIZZLE_B &&
|
||||
components->a == VK_COMPONENT_SWIZZLE_A)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (components->r == VK_COMPONENT_SWIZZLE_B && components->g == VK_COMPONENT_SWIZZLE_G && components->b == VK_COMPONENT_SWIZZLE_R &&
|
||||
components->a == VK_COMPONENT_SWIZZLE_A && image_info->format == VK_FORMAT_R8G8B8A8_SRGB)
|
||||
{
|
||||
printf("replace VK_FORMAT_R8G8B8A8_SRGB => VK_FORMAT_B8G8R8A8_SRGB\n");
|
||||
|
||||
components->r = VK_COMPONENT_SWIZZLE_R;
|
||||
components->g = VK_COMPONENT_SWIZZLE_G;
|
||||
components->b = VK_COMPONENT_SWIZZLE_B;
|
||||
components->a = VK_COMPONENT_SWIZZLE_A;
|
||||
image_info->format = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO() swizzle channels in shader
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* TextureObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("TextureObject::Create");
|
||||
|
||||
EXIT_IF(size == nullptr || vaddr == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto dfmt = params[PARAM_DFMT_NFMT] >> 32u;
|
||||
auto nfmt = params[PARAM_DFMT_NFMT] & 0xffffffffu;
|
||||
auto width = params[PARAM_WIDTH_HEIGHT] >> 32u;
|
||||
auto height = params[PARAM_WIDTH_HEIGHT] & 0xffffffffu;
|
||||
auto levels = params[PARAM_LEVELS];
|
||||
auto swizzle = params[PARAM_SWIZZLE];
|
||||
auto usage = params[PARAM_USAGE];
|
||||
|
||||
VkImageUsageFlags vk_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case TEXTURE_USAGE_SAMPLED: vk_usage |= VK_IMAGE_USAGE_SAMPLED_BIT; break;
|
||||
case TEXTURE_USAGE_STORAGE: vk_usage |= VK_IMAGE_USAGE_STORAGE_BIT; break;
|
||||
default: EXIT("unknown usage: %u\n", static_cast<uint32_t>(usage));
|
||||
}
|
||||
|
||||
VkComponentMapping components {};
|
||||
|
||||
components.r = get_swizzle(swizzle & 0xffu);
|
||||
components.g = get_swizzle((swizzle >> 8u) & 0xffu);
|
||||
components.b = get_swizzle((swizzle >> 16u) & 0xffu);
|
||||
components.a = get_swizzle((swizzle >> 24u) & 0xffu);
|
||||
|
||||
auto pixel_format = get_texture_format(dfmt, nfmt);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(pixel_format == VK_FORMAT_UNDEFINED);
|
||||
EXIT_NOT_IMPLEMENTED(width == 0);
|
||||
EXIT_NOT_IMPLEMENTED(height == 0);
|
||||
|
||||
auto* vk_obj = new TextureVulkanImage;
|
||||
|
||||
VkImageCreateInfo image_info {};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.pNext = nullptr;
|
||||
image_info.flags = 0;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = width;
|
||||
image_info.extent.height = height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = levels;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = pixel_format;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = vk_usage;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
if (!CheckSwizzle(ctx, &image_info, &components))
|
||||
{
|
||||
EXIT("swizzle is not supported");
|
||||
}
|
||||
|
||||
if (!CheckFormat(ctx, &image_info))
|
||||
{
|
||||
EXIT("format is not supported");
|
||||
}
|
||||
|
||||
vk_obj->extent.width = width;
|
||||
vk_obj->extent.height = height;
|
||||
vk_obj->format = image_info.format;
|
||||
vk_obj->image = nullptr;
|
||||
vk_obj->image_view = nullptr;
|
||||
vk_obj->layout = image_info.initialLayout;
|
||||
|
||||
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
|
||||
|
||||
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
|
||||
|
||||
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
bool allocated = VulkanAllocate(ctx, mem);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!allocated);
|
||||
|
||||
VulkanBindImageMemory(ctx, vk_obj, mem);
|
||||
|
||||
vk_obj->memory = *mem;
|
||||
|
||||
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
|
||||
|
||||
VkImageViewCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.image = vk_obj->image;
|
||||
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
create_info.format = vk_obj->format;
|
||||
create_info.components = components;
|
||||
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
create_info.subresourceRange.baseArrayLayer = 0;
|
||||
create_info.subresourceRange.baseMipLevel = 0;
|
||||
create_info.subresourceRange.layerCount = 1;
|
||||
create_info.subresourceRange.levelCount = 1;
|
||||
|
||||
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("TextureObject::update_func");
|
||||
|
||||
EXIT_IF(obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(params == nullptr);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
|
||||
|
||||
auto* vk_obj = static_cast<TextureVulkanImage*>(obj);
|
||||
|
||||
auto tile = params[TextureObject::PARAM_TILE];
|
||||
auto dfmt = params[TextureObject::PARAM_DFMT_NFMT] >> 32u;
|
||||
auto nfmt = params[TextureObject::PARAM_DFMT_NFMT] & 0xffffffffu;
|
||||
auto width = params[TextureObject::PARAM_WIDTH_HEIGHT] >> 32u;
|
||||
auto height = params[TextureObject::PARAM_WIDTH_HEIGHT] & 0xffffffffu;
|
||||
auto levels = params[TextureObject::PARAM_LEVELS];
|
||||
auto pitch = params[TextureObject::PARAM_PITCH];
|
||||
auto usage = params[TextureObject::PARAM_USAGE];
|
||||
bool neo = Config::IsNeo();
|
||||
|
||||
VkImageLayout vk_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case TextureObject::TEXTURE_USAGE_SAMPLED: vk_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break;
|
||||
case TextureObject::TEXTURE_USAGE_STORAGE: vk_layout = VK_IMAGE_LAYOUT_GENERAL; break;
|
||||
default: EXIT("unknown usage: %u\n", static_cast<uint32_t>(usage));
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(levels >= 16);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(tile != 8 && tile != 13);
|
||||
|
||||
uint32_t level_sizes[16];
|
||||
|
||||
TileGetTextureSize(dfmt, nfmt, width, height, pitch, levels, tile, neo, nullptr, level_sizes, nullptr, nullptr);
|
||||
|
||||
// dbg_test_mipmaps(ctx, VK_FORMAT_BC3_SRGB_BLOCK, 512, 512);
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint32_t mip_width = width;
|
||||
uint32_t mip_height = height;
|
||||
uint32_t mip_pitch = pitch;
|
||||
|
||||
Vector<BufferImageCopy> regions(levels);
|
||||
for (uint32_t i = 0; i < levels; i++)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(level_sizes[i] == 0);
|
||||
|
||||
regions[i].offset = offset;
|
||||
regions[i].width = mip_width;
|
||||
regions[i].height = mip_height;
|
||||
regions[i].pitch = mip_pitch;
|
||||
|
||||
offset += level_sizes[i];
|
||||
|
||||
if (mip_width > 1)
|
||||
{
|
||||
mip_width /= 2;
|
||||
}
|
||||
if (mip_height > 1)
|
||||
{
|
||||
mip_height /= 2;
|
||||
}
|
||||
if (mip_pitch > 1)
|
||||
{
|
||||
mip_pitch /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (tile == 13)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(pitch != width);
|
||||
auto* temp_buf = new uint8_t[*size];
|
||||
TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::TextureTiled, dfmt, nfmt, width, height, levels, neo);
|
||||
UtilFillImage(ctx, vk_obj, temp_buf, *size, regions, static_cast<uint64_t>(vk_layout));
|
||||
delete[] temp_buf;
|
||||
} else if (tile == 8)
|
||||
{
|
||||
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size, regions, static_cast<uint64_t>(vk_layout));
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_DFMT_NFMT] == other[PARAM_DFMT_NFMT] && params[PARAM_PITCH] == other[PARAM_PITCH] &&
|
||||
params[PARAM_WIDTH_HEIGHT] == other[PARAM_WIDTH_HEIGHT] && params[PARAM_USAGE] == other[PARAM_USAGE] &&
|
||||
params[PARAM_LEVELS] == other[PARAM_LEVELS] && params[PARAM_TILE] == other[PARAM_TILE] &&
|
||||
params[PARAM_NEO] == other[PARAM_NEO] && params[PARAM_SWIZZLE] == other[PARAM_SWIZZLE]);
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("TextureObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<TextureVulkanImage*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
DeleteDescriptor(vk_obj);
|
||||
|
||||
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
|
||||
|
||||
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
|
||||
|
||||
VulkanFree(ctx, mem);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t TextureObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t TextureObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "Emulator/Graphics/Objects/VertexBuffer.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* VertexBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
|
||||
VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto* vk_obj = new VulkanBuffer;
|
||||
|
||||
vk_obj->usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||
vk_obj->memory.property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
vk_obj->buffer = nullptr;
|
||||
|
||||
VulkanCreateBuffer(ctx, *size, vk_obj);
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
|
||||
|
||||
VulkanBuffer staging_buffer {};
|
||||
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
VulkanCreateBuffer(ctx, *size, &staging_buffer);
|
||||
EXIT_NOT_IMPLEMENTED(staging_buffer.buffer == nullptr);
|
||||
|
||||
void* data = nullptr;
|
||||
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, *size, 0, &data);
|
||||
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
|
||||
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
|
||||
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
|
||||
VulkanUnmapMemory(ctx, &staging_buffer.memory);
|
||||
|
||||
UtilCopyBuffer(&staging_buffer, vk_obj, *size);
|
||||
|
||||
VulkanDeleteBuffer(ctx, &staging_buffer);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
|
||||
const uint64_t* /*size*/, int /*vaddr_num*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::update_func");
|
||||
|
||||
KYTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
bool VertexBufferGpuObject::Equal(const uint64_t* /*other*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(vk_obj->buffer == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
VulkanDeleteBuffer(ctx, vk_obj);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t VertexBufferGpuObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t VertexBufferGpuObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "Emulator/Graphics/Objects/VideoOutBuffer.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Graphics/Tile.h"
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void* VideoOutBufferObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VideoOutBufferObject::Create");
|
||||
|
||||
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
auto pixel_format = params[PARAM_FORMAT];
|
||||
auto width = params[PARAM_WIDTH];
|
||||
auto height = params[PARAM_HEIGHT];
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(pixel_format != 0x80000000);
|
||||
EXIT_NOT_IMPLEMENTED(width == 0);
|
||||
EXIT_NOT_IMPLEMENTED(height == 0);
|
||||
|
||||
auto* vk_obj = new VideoOutVulkanImage;
|
||||
|
||||
vk_obj->extent.width = width;
|
||||
vk_obj->extent.height = height;
|
||||
vk_obj->format = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
vk_obj->image = nullptr;
|
||||
vk_obj->image_view = nullptr;
|
||||
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImageCreateInfo image_info {};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.pNext = nullptr;
|
||||
image_info.flags = 0;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = vk_obj->extent.width;
|
||||
image_info.extent.height = vk_obj->extent.height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = vk_obj->format;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = vk_obj->layout;
|
||||
image_info.usage = static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
|
||||
|
||||
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
|
||||
|
||||
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
bool allocated = VulkanAllocate(ctx, mem);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!allocated);
|
||||
|
||||
VulkanBindImageMemory(ctx, vk_obj, mem);
|
||||
|
||||
vk_obj->memory = *mem;
|
||||
|
||||
printf("VideoOutBufferObject::Create()\n");
|
||||
printf("\t mem->requirements.size = %" PRIu64 "\n", mem->requirements.size);
|
||||
printf("\t width = %" PRIu64 "\n", width);
|
||||
printf("\t height = %" PRIu64 "\n", height);
|
||||
printf("\t size = %" PRIu64 "\n", *size);
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
|
||||
|
||||
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
|
||||
|
||||
VkImageViewCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.image = vk_obj->image;
|
||||
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
create_info.format = vk_obj->format;
|
||||
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
create_info.subresourceRange.baseArrayLayer = 0;
|
||||
create_info.subresourceRange.baseMipLevel = 0;
|
||||
create_info.subresourceRange.layerCount = 1;
|
||||
create_info.subresourceRange.levelCount = 1;
|
||||
|
||||
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
|
||||
|
||||
return vk_obj;
|
||||
}
|
||||
|
||||
static bool buffer_is_tiled(uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
if ((size & 0x7u) == 0)
|
||||
{
|
||||
const auto* ptr = reinterpret_cast<const uint64_t*>(vaddr);
|
||||
const auto* ptr_end = reinterpret_cast<const uint64_t*>(vaddr + size / 8);
|
||||
for (uint64_t element = *ptr; ptr < ptr_end; ptr++)
|
||||
{
|
||||
if (element != *ptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void update_func(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VideoOutBufferObject::update_func");
|
||||
|
||||
EXIT_IF(obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(params == nullptr);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
|
||||
|
||||
auto* vk_obj = static_cast<VideoOutVulkanImage*>(obj);
|
||||
|
||||
bool tiled = (params[VideoOutBufferObject::PARAM_TILED] != 0);
|
||||
bool neo = (params[VideoOutBufferObject::PARAM_NEO] != 0);
|
||||
auto pitch = params[VideoOutBufferObject::PARAM_PITCH];
|
||||
auto width = params[VideoOutBufferObject::PARAM_WIDTH];
|
||||
auto height = params[VideoOutBufferObject::PARAM_HEIGHT];
|
||||
|
||||
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
if (tiled && buffer_is_tiled(*vaddr, *size))
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(width != pitch);
|
||||
auto* temp_buf = new uint8_t[*size];
|
||||
TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::VideoOutTiled, width, height, neo);
|
||||
UtilFillImage(ctx, vk_obj, temp_buf, *size, pitch);
|
||||
delete[] temp_buf;
|
||||
} else
|
||||
{
|
||||
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoOutBufferObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_FORMAT] == other[PARAM_FORMAT] && params[PARAM_WIDTH] == other[PARAM_WIDTH] &&
|
||||
params[PARAM_HEIGHT] == other[PARAM_HEIGHT] && params[PARAM_TILED] == other[PARAM_TILED] &&
|
||||
params[PARAM_PITCH] == other[PARAM_PITCH] && params[PARAM_NEO] == other[PARAM_NEO]);
|
||||
}
|
||||
|
||||
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("VideoOutBufferObject::delete_func");
|
||||
|
||||
auto* vk_obj = reinterpret_cast<VideoOutVulkanImage*>(obj);
|
||||
|
||||
EXIT_IF(vk_obj == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
// if (vk_obj->framebuffer != nullptr)
|
||||
{
|
||||
DeleteFramebuffer(vk_obj);
|
||||
}
|
||||
|
||||
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
|
||||
|
||||
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
|
||||
|
||||
VulkanFree(ctx, mem);
|
||||
|
||||
delete vk_obj;
|
||||
}
|
||||
|
||||
GpuObject::delete_func_t VideoOutBufferObject::GetDeleteFunc() const
|
||||
{
|
||||
return delete_func;
|
||||
}
|
||||
|
||||
GpuObject::update_func_t VideoOutBufferObject::GetUpdateFunc() const
|
||||
{
|
||||
return update_func;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
Reference in New Issue
Block a user