mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
#include "Emulator/Graphics/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;
|
||||
|
||||
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 = 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);
|
||||
|
||||
UtilSetImageLayoutOptimal(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
|
||||
@@ -0,0 +1,923 @@
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/MagicEnum.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Graphics/GraphicContext.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
//#define XXH_INLINE_ALL
|
||||
#include <xxhash/xxhash.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
class GpuMemory
|
||||
{
|
||||
public:
|
||||
GpuMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~GpuMemory() { KYTY_NOT_IMPLEMENTED; }
|
||||
KYTY_CLASS_NO_COPY(GpuMemory);
|
||||
|
||||
bool IsAllocated(uint64_t vaddr, uint64_t size);
|
||||
void SetAllocatedRange(uint64_t vaddr, uint64_t size);
|
||||
void Free(GraphicContext* ctx, uint64_t vaddr, uint64_t size);
|
||||
|
||||
void* GetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info);
|
||||
void ResetHash(GraphicContext* ctx, uint64_t* vaddr, uint64_t* size, int vaddr_num, GpuMemoryObjectType type);
|
||||
void FrameDone();
|
||||
void WriteBack(GraphicContext* ctx);
|
||||
|
||||
void DbgDump();
|
||||
|
||||
private:
|
||||
static constexpr int OBJ_OVERLAPS_MAX = 2;
|
||||
static constexpr int VADDR_BLOCKS_MAX = 3;
|
||||
|
||||
struct AllocatedRange
|
||||
{
|
||||
uint64_t vaddr;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct ObjectInfo
|
||||
{
|
||||
void* obj = nullptr;
|
||||
uint64_t params[GpuObject::PARAMS_MAX] = {};
|
||||
GpuMemoryObjectType type = GpuMemoryObjectType::Invalid;
|
||||
uint64_t hash[VADDR_BLOCKS_MAX] = {};
|
||||
GpuObject::write_back_func_t write_back_func = nullptr;
|
||||
GpuObject::delete_func_t delete_func = nullptr;
|
||||
GpuObject::update_func_t update_func = nullptr;
|
||||
uint64_t use_last_frame = 0;
|
||||
uint64_t use_num = 0;
|
||||
bool in_use = false;
|
||||
bool read_only = false;
|
||||
bool check_hash = false;
|
||||
VulkanMemory mem;
|
||||
};
|
||||
|
||||
struct Object
|
||||
{
|
||||
uint64_t vaddr[VADDR_BLOCKS_MAX] = {};
|
||||
uint64_t size[VADDR_BLOCKS_MAX] = {};
|
||||
int vaddr_num = 0;
|
||||
ObjectInfo overlaps[OBJ_OVERLAPS_MAX];
|
||||
int overlaps_num = 0;
|
||||
bool free = true;
|
||||
};
|
||||
|
||||
void Free(GraphicContext* ctx, Object& h);
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
|
||||
Vector<AllocatedRange> m_allocated;
|
||||
Vector<Object> m_objects;
|
||||
uint64_t m_objects_size = 0;
|
||||
uint64_t m_current_frame = 0;
|
||||
};
|
||||
|
||||
class GpuResources
|
||||
{
|
||||
public:
|
||||
struct Info
|
||||
{
|
||||
uint32_t owner = 0;
|
||||
bool free = true;
|
||||
uint64_t memory = 0;
|
||||
size_t size = 0;
|
||||
String name;
|
||||
uint32_t type = 0;
|
||||
uint64_t user_data = 0;
|
||||
};
|
||||
|
||||
GpuResources() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~GpuResources() { KYTY_NOT_IMPLEMENTED; }
|
||||
KYTY_CLASS_NO_COPY(GpuResources);
|
||||
|
||||
uint32_t AddOwner(const String& name);
|
||||
uint32_t AddResource(uint32_t owner_handle, uint64_t memory, size_t size, const String& name, uint32_t type, uint64_t user_data);
|
||||
void DeleteOwner(uint32_t owner_handle);
|
||||
void DeleteResources(uint32_t owner_handle);
|
||||
void DeleteResource(uint32_t resource_handle);
|
||||
|
||||
bool FindInfo(uint64_t memory, Info* dst);
|
||||
|
||||
private:
|
||||
struct Owner
|
||||
{
|
||||
String name;
|
||||
bool free = true;
|
||||
};
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
|
||||
Vector<Owner> m_owners;
|
||||
Vector<Info> m_infos;
|
||||
};
|
||||
|
||||
static GpuMemory* g_gpu_memory = nullptr;
|
||||
static GpuResources* g_gpu_resources = nullptr;
|
||||
|
||||
uint32_t GpuResources::AddOwner(const String& name)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
Owner n;
|
||||
n.name = name;
|
||||
n.free = false;
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto& b: m_owners)
|
||||
{
|
||||
if (b.free)
|
||||
{
|
||||
b = n;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
m_owners.Add(n);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
uint32_t GpuResources::AddResource(uint32_t owner_handle, uint64_t memory, size_t size, const String& name, uint32_t type,
|
||||
uint64_t user_data)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
|
||||
EXIT_NOT_IMPLEMENTED(memory == 0);
|
||||
|
||||
Info info;
|
||||
info.owner = owner_handle;
|
||||
info.memory = memory;
|
||||
info.free = false;
|
||||
info.name = name;
|
||||
info.size = size;
|
||||
info.type = type;
|
||||
info.user_data = user_data;
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto& i: m_infos)
|
||||
{
|
||||
if (i.free)
|
||||
{
|
||||
i = info;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
m_infos.Add(info);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void GpuResources::DeleteOwner(uint32_t owner_handle)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
|
||||
|
||||
for (auto& i: m_infos)
|
||||
{
|
||||
if (!i.free && i.owner == owner_handle)
|
||||
{
|
||||
i.free = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(m_owners[owner_handle].free);
|
||||
|
||||
m_owners[owner_handle].free = true;
|
||||
}
|
||||
|
||||
void GpuResources::DeleteResources(uint32_t owner_handle)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
|
||||
|
||||
for (auto& i: m_infos)
|
||||
{
|
||||
if (!i.free && i.owner == owner_handle)
|
||||
{
|
||||
i.free = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuResources::DeleteResource(uint32_t resource_handle)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!m_infos.IndexValid(resource_handle));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(m_infos[resource_handle].free);
|
||||
|
||||
m_infos[resource_handle].free = true;
|
||||
}
|
||||
|
||||
bool GpuResources::FindInfo(uint64_t memory, Info* dst)
|
||||
{
|
||||
EXIT_IF(dst == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
// NOLINTNEXTLINE(readability-use-anyofallof)
|
||||
for (const auto& i: m_infos)
|
||||
{
|
||||
if (!i.free && memory >= i.memory && memory < i.memory + i.size)
|
||||
{
|
||||
*dst = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GpuMemory::SetAllocatedRange(uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
EXIT_IF(size == 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(IsAllocated(vaddr, size));
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
AllocatedRange r {};
|
||||
r.vaddr = vaddr;
|
||||
r.size = size;
|
||||
|
||||
m_allocated.Add(r);
|
||||
}
|
||||
|
||||
bool GpuMemory::IsAllocated(uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
EXIT_IF(size == 0);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
return std::any_of(m_allocated.begin(), m_allocated.end(),
|
||||
[vaddr, size](auto& r) {
|
||||
return ((vaddr >= r.vaddr && vaddr < r.vaddr + r.size) ||
|
||||
((vaddr + size - 1) >= r.vaddr && (vaddr + size - 1) < r.vaddr + r.size));
|
||||
});
|
||||
}
|
||||
|
||||
static uint64_t calc_hash(const uint8_t* buf, uint64_t size)
|
||||
{
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
return (size > 0 && buf != nullptr ? XXH64(buf, size, 0) : 0);
|
||||
}
|
||||
|
||||
static bool vaddr_equal(const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const uint64_t* vaddr2, const uint64_t* size2,
|
||||
int vaddr_num2)
|
||||
{
|
||||
if (vaddr_num != vaddr_num2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < vaddr_num; i++)
|
||||
{
|
||||
if (vaddr[i] != vaddr2[i] || size[i] != size2[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vaddr_overlap(const uint64_t* hvaddr, const uint64_t* hsize, int vaddr_num, uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
for (int i = 0; i < vaddr_num; i++)
|
||||
{
|
||||
if ((vaddr >= hvaddr[i] && vaddr < hvaddr[i] + hsize[i]) ||
|
||||
((vaddr + size - 1) >= hvaddr[i] && (vaddr + size - 1) < hvaddr[i] + hsize[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void* GpuMemory::GetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info)
|
||||
{
|
||||
EXIT_IF(info.type == GpuMemoryObjectType::Invalid);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num > VADDR_BLOCKS_MAX || vaddr_num <= 0);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint64_t hash[VADDR_BLOCKS_MAX] = {};
|
||||
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
EXIT_IF(size[vi] == 0);
|
||||
|
||||
if (info.check_hash)
|
||||
{
|
||||
hash[vi] = calc_hash(reinterpret_cast<const uint8_t*>(vaddr[vi]), size[vi]);
|
||||
} else
|
||||
{
|
||||
hash[vi] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Object* update_object = nullptr;
|
||||
|
||||
for (auto& h: m_objects)
|
||||
{
|
||||
if (!h.free && vaddr_equal(h.vaddr, h.size, h.vaddr_num, vaddr, size, vaddr_num))
|
||||
{
|
||||
for (int oi = 0; oi < h.overlaps_num; oi++)
|
||||
{
|
||||
auto& o = h.overlaps[oi];
|
||||
if (o.type == info.type && info.Equal(o.params))
|
||||
{
|
||||
bool need_update = false;
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
if (o.hash[vi] != hash[vi])
|
||||
{
|
||||
printf("Update (CPU -> GPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
|
||||
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi]);
|
||||
need_update = true;
|
||||
o.hash[vi] = hash[vi];
|
||||
}
|
||||
}
|
||||
if (need_update)
|
||||
{
|
||||
EXIT_IF(o.update_func == nullptr);
|
||||
o.update_func(ctx, o.params, o.obj, vaddr, size, vaddr_num);
|
||||
}
|
||||
o.use_num++;
|
||||
o.use_last_frame = m_current_frame;
|
||||
o.in_use = true;
|
||||
o.read_only = info.read_only;
|
||||
o.check_hash = info.check_hash;
|
||||
return o.obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (h.overlaps_num == 1 &&
|
||||
(h.overlaps[0].type == GpuMemoryObjectType::VideoOutBuffer && info.type == GpuMemoryObjectType::StorageBuffer))
|
||||
{
|
||||
update_object = &h;
|
||||
break;
|
||||
}
|
||||
|
||||
// EXIT("not implemented");
|
||||
|
||||
Free(ctx, h);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(!h.free && vaddr_overlap(h.vaddr, h.size, h.overlaps_num, vaddr[vi], size[vi]));
|
||||
}
|
||||
}
|
||||
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(!IsAllocated(vaddr[vi], size[vi]));
|
||||
}
|
||||
|
||||
ObjectInfo o {};
|
||||
|
||||
for (int i = 0; i < GpuObject::PARAMS_MAX; i++)
|
||||
{
|
||||
o.params[i] = info.params[i];
|
||||
}
|
||||
|
||||
o.type = info.type;
|
||||
o.obj = nullptr;
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
o.hash[vi] = hash[vi];
|
||||
}
|
||||
o.obj = info.Create(ctx, vaddr, size, vaddr_num, &o.mem);
|
||||
o.write_back_func = info.GetWriteBackFunc();
|
||||
o.delete_func = info.GetDeleteFunc();
|
||||
o.update_func = info.GetUpdateFunc();
|
||||
o.use_num = 1;
|
||||
o.use_last_frame = m_current_frame;
|
||||
o.in_use = true;
|
||||
o.read_only = info.read_only;
|
||||
o.check_hash = info.check_hash;
|
||||
|
||||
bool updated = false;
|
||||
|
||||
if (update_object != nullptr)
|
||||
{
|
||||
EXIT_IF(update_object->overlaps_num >= OBJ_OVERLAPS_MAX);
|
||||
update_object->overlaps[update_object->overlaps_num++] = o;
|
||||
|
||||
updated = true;
|
||||
} else
|
||||
{
|
||||
for (auto& u: m_objects)
|
||||
{
|
||||
if (u.free)
|
||||
{
|
||||
u.overlaps_num = 1;
|
||||
u.overlaps[0] = o;
|
||||
u.free = false;
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
u.vaddr[vi] = vaddr[vi];
|
||||
u.size[vi] = size[vi];
|
||||
m_objects_size += size[vi];
|
||||
}
|
||||
u.vaddr_num = vaddr_num;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
Object h {};
|
||||
for (int vi = 0; vi < vaddr_num; vi++)
|
||||
{
|
||||
h.vaddr[vi] = vaddr[vi];
|
||||
h.size[vi] = size[vi];
|
||||
m_objects_size += size[vi];
|
||||
}
|
||||
h.vaddr_num = vaddr_num;
|
||||
h.overlaps_num = 1;
|
||||
h.overlaps[0] = o;
|
||||
h.free = false;
|
||||
m_objects.Add(h);
|
||||
}
|
||||
|
||||
return o.obj;
|
||||
}
|
||||
|
||||
void GpuMemory::ResetHash(GraphicContext* /*ctx*/, uint64_t* vaddr, uint64_t* size, int vaddr_num, GpuMemoryObjectType type)
|
||||
{
|
||||
EXIT_IF(type == GpuMemoryObjectType::Invalid);
|
||||
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num > VADDR_BLOCKS_MAX || vaddr_num <= 0);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint64_t new_hash = 0;
|
||||
|
||||
for (auto& h: m_objects)
|
||||
{
|
||||
if (!h.free && vaddr_equal(h.vaddr, h.size, h.vaddr_num, vaddr, size, vaddr_num))
|
||||
{
|
||||
for (int oi = 0; oi < h.overlaps_num; oi++)
|
||||
{
|
||||
auto& o = h.overlaps[oi];
|
||||
if (o.type == type)
|
||||
{
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
printf("ResetHash: type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 ", old_hash = 0x%016" PRIx64
|
||||
", new_hash = 0x%016" PRIx64 "\n",
|
||||
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi], o.hash[vi], new_hash);
|
||||
|
||||
o.hash[vi] = new_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemory::Free(GraphicContext* ctx, uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
printf("Release gpu objects:\n");
|
||||
printf("\t gpu_vaddr = 0x%016" PRIx64 "\n", vaddr);
|
||||
printf("\t size = 0x%016" PRIx64 "\n", size);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!IsAllocated(vaddr, size));
|
||||
|
||||
int index = 0;
|
||||
for (auto& a: m_allocated)
|
||||
{
|
||||
if (a.vaddr == vaddr && a.size == size)
|
||||
{
|
||||
m_allocated.RemoveAt(index);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(IsAllocated(vaddr, size));
|
||||
|
||||
for (auto& h: m_objects)
|
||||
{
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
if (!h.free && (h.vaddr[vi] >= vaddr && h.vaddr[vi] < vaddr + size))
|
||||
{
|
||||
Free(ctx, h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemory::Free(GraphicContext* ctx, Object& h)
|
||||
{
|
||||
for (int oi = 0; oi < h.overlaps_num; oi++)
|
||||
{
|
||||
auto& o = h.overlaps[oi];
|
||||
|
||||
EXIT_IF(o.delete_func == nullptr);
|
||||
|
||||
if (o.delete_func != nullptr)
|
||||
{
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
printf("Delete: type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n", Core::EnumName(o.type).C_Str(),
|
||||
h.vaddr[vi], h.size[vi]);
|
||||
}
|
||||
|
||||
o.delete_func(ctx, o.obj, &o.mem);
|
||||
}
|
||||
}
|
||||
h.overlaps_num = 0;
|
||||
h.free = true;
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
m_objects_size -= h.size[vi];
|
||||
}
|
||||
h.vaddr_num = 0;
|
||||
}
|
||||
|
||||
void GpuMemory::FrameDone()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
m_current_frame++;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void GpuMemory::WriteBack(GraphicContext* ctx)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& h: m_objects)
|
||||
{
|
||||
if (!h.free)
|
||||
{
|
||||
for (int oi = 0; oi < h.overlaps_num; oi++)
|
||||
{
|
||||
auto& o = h.overlaps[oi];
|
||||
if (o.in_use && /*o.use_last_frame >= m_current_frame &&*/ o.write_back_func != nullptr && !o.read_only)
|
||||
{
|
||||
o.write_back_func(ctx, o.obj, h.vaddr, h.size, h.vaddr_num);
|
||||
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
uint64_t new_hash = 0;
|
||||
|
||||
if (o.check_hash)
|
||||
{
|
||||
new_hash = calc_hash(reinterpret_cast<const uint8_t*>(h.vaddr[vi]), h.size[vi]);
|
||||
}
|
||||
|
||||
printf("WriteBack (GPU -> CPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64
|
||||
", old_hash = 0x%016" PRIx64 ", new_hash = 0x%016" PRIx64 "\n",
|
||||
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi], o.hash[vi], new_hash);
|
||||
|
||||
o.hash[vi] = new_hash;
|
||||
}
|
||||
|
||||
for (int oi2 = 0; oi2 < h.overlaps_num; oi2++)
|
||||
{
|
||||
if (oi2 != oi)
|
||||
{
|
||||
auto& o2 = h.overlaps[oi2];
|
||||
|
||||
bool need_update = false;
|
||||
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
uint64_t hash = o.hash[vi];
|
||||
|
||||
if (o2.hash[vi] != hash)
|
||||
{
|
||||
printf("Update (CPU -> GPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64
|
||||
", old_hash = 0x%016" PRIx64 ", new_hash = 0x%016" PRIx64 "\n",
|
||||
Core::EnumName(o2.type).C_Str(), h.vaddr[vi], h.size[vi], o2.hash[vi], hash);
|
||||
o2.hash[vi] = hash;
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_update)
|
||||
{
|
||||
EXIT_IF(o2.update_func == nullptr);
|
||||
|
||||
o2.update_func(ctx, o2.params, o2.obj, h.vaddr, h.size, h.vaddr_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o.in_use = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemory::DbgDump()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
printf("--- Gpu Memory ---\n");
|
||||
|
||||
for (auto& o: m_allocated)
|
||||
{
|
||||
printf("Allocated block: vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n", o.vaddr, o.size);
|
||||
}
|
||||
|
||||
printf("m_current_frame = %" PRIu64 "\n", m_current_frame);
|
||||
printf("m_objects_size = %" PRIu64 "\n", m_objects_size);
|
||||
|
||||
for (auto& h: m_objects)
|
||||
{
|
||||
if (!h.free)
|
||||
{
|
||||
printf("Object:\n");
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
printf("\t vaddr = 0x%016" PRIx64 "\n", h.vaddr[vi]);
|
||||
printf("\t size = 0x%016" PRIx64 "\n", h.size[vi]);
|
||||
GpuResources::Info res_info;
|
||||
if (g_gpu_resources->FindInfo(h.vaddr[vi], &res_info))
|
||||
{
|
||||
printf("\t {\n");
|
||||
printf("\t\t RegisteredResource: %s\n", res_info.name.C_Str());
|
||||
printf("\t\t addr: %016" PRIx64 "\n", res_info.memory);
|
||||
printf("\t\t size: %" PRIu64 "\n", res_info.size);
|
||||
printf("\t\t type: %" PRIu32 "\n", res_info.type);
|
||||
printf("\t\t user_data: %" PRIu64 "\n", res_info.user_data);
|
||||
printf("\t }\n");
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(res_info.size != h.size[vi]);
|
||||
// EXIT_NOT_IMPLEMENTED(res_info.memory != h.vaddr[vi]);
|
||||
}
|
||||
}
|
||||
printf("\t overlaps_num = %d\n", h.overlaps_num);
|
||||
for (int oi = 0; oi < h.overlaps_num; oi++)
|
||||
{
|
||||
auto& o = h.overlaps[oi];
|
||||
printf("\t [%d] type = %s\n", oi, Core::EnumName(o.type).C_Str());
|
||||
for (int vi = 0; vi < h.vaddr_num; vi++)
|
||||
{
|
||||
printf("\t [%d] hash = 0x%016" PRIx64 "\n", oi, o.hash[vi]);
|
||||
}
|
||||
printf("\t [%d] vk_size = 0x%016" PRIx64 "\n", oi, o.mem.requirements.size);
|
||||
printf("\t [%d] vk_align = 0x%016" PRIx64 "\n", oi, o.mem.requirements.alignment);
|
||||
printf("\t [%d] vk_type = 0x%08" PRIx32 "\n", oi, o.mem.type);
|
||||
printf("\t [%d] use_last_frame = %" PRIu64 "\n", oi, o.use_last_frame);
|
||||
printf("\t [%d] use_num = %" PRIu64 "\n", oi, o.use_num);
|
||||
printf("\t [%d] in_use = %s\n", oi, o.in_use ? "true" : "false");
|
||||
printf("\t [%d] read_only = %s\n", oi, o.read_only ? "true" : "false");
|
||||
printf("\t [%d] check_hash = %s\n", oi, o.check_hash ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemoryInit()
|
||||
{
|
||||
EXIT_IF(g_gpu_memory != nullptr);
|
||||
EXIT_IF(g_gpu_resources != nullptr);
|
||||
|
||||
g_gpu_memory = new GpuMemory;
|
||||
g_gpu_resources = new GpuResources;
|
||||
}
|
||||
|
||||
void GpuMemorySetAllocatedRange(uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
|
||||
g_gpu_memory->SetAllocatedRange(vaddr, size);
|
||||
}
|
||||
|
||||
void GpuMemoryFree(GraphicContext* ctx, uint64_t vaddr, uint64_t size)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
g_gpu_memory->Free(ctx, vaddr, size);
|
||||
}
|
||||
|
||||
void* GpuMemoryGetObject(GraphicContext* ctx, uint64_t vaddr, uint64_t size, const GpuObject& info)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
return g_gpu_memory->GetObject(ctx, &vaddr, &size, 1, info);
|
||||
}
|
||||
|
||||
void* GpuMemoryGetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
return g_gpu_memory->GetObject(ctx, vaddr, size, vaddr_num, info);
|
||||
}
|
||||
|
||||
void GpuMemoryResetHash(GraphicContext* ctx, uint64_t vaddr, uint64_t size, GpuMemoryObjectType type)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
g_gpu_memory->ResetHash(ctx, &vaddr, &size, 1, type);
|
||||
}
|
||||
|
||||
void GpuMemoryDbgDump()
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
|
||||
g_gpu_memory->DbgDump();
|
||||
}
|
||||
|
||||
void GpuMemoryFlush()
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
|
||||
// TODO(): update vulkan objects after CPU-drawing
|
||||
}
|
||||
|
||||
void GpuMemoryFrameDone()
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
|
||||
g_gpu_memory->FrameDone();
|
||||
}
|
||||
|
||||
void GpuMemoryWriteBack(GraphicContext* ctx)
|
||||
{
|
||||
EXIT_IF(g_gpu_memory == nullptr);
|
||||
EXIT_IF(ctx == nullptr);
|
||||
|
||||
g_gpu_memory->WriteBack(ctx);
|
||||
}
|
||||
|
||||
bool VulkanAllocate(GraphicContext* ctx, VulkanMemory* mem)
|
||||
{
|
||||
static std::atomic<uint64_t> seq = 0;
|
||||
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(mem->memory != nullptr);
|
||||
EXIT_IF(mem->requirements.size == 0);
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memory_properties {};
|
||||
vkGetPhysicalDeviceMemoryProperties(ctx->physical_device, &memory_properties);
|
||||
|
||||
uint32_t index = 0;
|
||||
for (; index < memory_properties.memoryTypeCount; index++)
|
||||
{
|
||||
if ((mem->requirements.memoryTypeBits & (static_cast<uint32_t>(1) << index)) != 0 &&
|
||||
(memory_properties.memoryTypes[index].propertyFlags & mem->property) == mem->property)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mem->type = index;
|
||||
mem->offset = 0;
|
||||
|
||||
VkMemoryAllocateInfo alloc_info {};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.pNext = nullptr;
|
||||
alloc_info.allocationSize = mem->requirements.size;
|
||||
alloc_info.memoryTypeIndex = index;
|
||||
|
||||
mem->unique_id = ++seq;
|
||||
|
||||
return (vkAllocateMemory(ctx->device, &alloc_info, nullptr, &mem->memory) == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanFree(GraphicContext* ctx, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
|
||||
vkFreeMemory(ctx->device, mem->memory, nullptr);
|
||||
|
||||
mem->memory = nullptr;
|
||||
}
|
||||
|
||||
void VulkanMapMemory(GraphicContext* ctx, VulkanMemory* mem, void** data)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(data == nullptr);
|
||||
|
||||
vkMapMemory(ctx->device, mem->memory, mem->offset, mem->requirements.size, 0, data);
|
||||
}
|
||||
|
||||
void VulkanUnmapMemory(GraphicContext* ctx, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
|
||||
vkUnmapMemory(ctx->device, mem->memory);
|
||||
}
|
||||
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, TextureVulkanImage* image, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(image == nullptr);
|
||||
|
||||
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
|
||||
}
|
||||
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, VideoOutVulkanImage* image, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(image == nullptr);
|
||||
|
||||
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
|
||||
}
|
||||
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, DepthStencilVulkanImage* image, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(image == nullptr);
|
||||
|
||||
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
|
||||
}
|
||||
|
||||
void VulkanBindBufferMemory(GraphicContext* ctx, VulkanBuffer* buffer, VulkanMemory* mem)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(mem == nullptr);
|
||||
EXIT_IF(buffer == nullptr);
|
||||
|
||||
vkBindBufferMemory(ctx->device, buffer->buffer, mem->memory, mem->offset);
|
||||
}
|
||||
|
||||
void GpuMemoryRegisterOwner(uint32_t* owner_handle, const char* name)
|
||||
{
|
||||
EXIT_IF(g_gpu_resources == nullptr);
|
||||
EXIT_IF(owner_handle == nullptr);
|
||||
EXIT_IF(name == nullptr);
|
||||
|
||||
*owner_handle = g_gpu_resources->AddOwner(String::FromUtf8(name));
|
||||
}
|
||||
|
||||
void GpuMemoryRegisterResource(uint32_t* resource_handle, uint32_t owner_handle, const void* memory, size_t size, const char* name,
|
||||
uint32_t type, uint64_t user_data)
|
||||
{
|
||||
EXIT_IF(g_gpu_resources == nullptr);
|
||||
EXIT_IF(resource_handle == nullptr);
|
||||
EXIT_IF(name == nullptr);
|
||||
|
||||
*resource_handle =
|
||||
g_gpu_resources->AddResource(owner_handle, reinterpret_cast<uint64_t>(memory), size, String::FromUtf8(name), type, user_data);
|
||||
}
|
||||
|
||||
void GpuMemoryUnregisterAllResourcesForOwner(uint32_t owner_handle)
|
||||
{
|
||||
EXIT_IF(g_gpu_resources == nullptr);
|
||||
|
||||
g_gpu_resources->DeleteResources(owner_handle);
|
||||
}
|
||||
|
||||
void GpuMemoryUnregisterOwnerAndResources(uint32_t owner_handle)
|
||||
{
|
||||
EXIT_IF(g_gpu_resources == nullptr);
|
||||
|
||||
g_gpu_resources->DeleteOwner(owner_handle);
|
||||
}
|
||||
|
||||
void GpuMemoryUnregisterResource(uint32_t resource_handle)
|
||||
{
|
||||
EXIT_IF(g_gpu_resources == nullptr);
|
||||
|
||||
g_gpu_resources->DeleteResource(resource_handle);
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,544 @@
|
||||
#include "Emulator/Graphics/Graphics.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/File.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
|
||||
#include "Emulator/Config.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Graphics/GraphicsRun.h"
|
||||
#include "Emulator/Graphics/HardwareContext.h"
|
||||
#include "Emulator/Graphics/Label.h"
|
||||
#include "Emulator/Graphics/Pm4.h"
|
||||
#include "Emulator/Graphics/Tile.h"
|
||||
#include "Emulator/Graphics/VideoOut.h"
|
||||
#include "Emulator/Graphics/Window.h"
|
||||
#include "Emulator/Kernel/Pthread.h"
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
LIB_NAME("GraphicsDriver", "GraphicsDriver");
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Graphics)
|
||||
{
|
||||
auto width = Config::GetScreenWidth();
|
||||
auto height = Config::GetScreenHeight();
|
||||
|
||||
WindowInit(width, height);
|
||||
VideoOut::VideoOutInit(width, height);
|
||||
GraphicsRenderInit();
|
||||
GraphicsRunInit();
|
||||
GpuMemoryInit();
|
||||
LabelInit();
|
||||
TileInit();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Graphics) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Graphics) {}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSetVsShader(uint32_t* cmd, uint64_t size, const VsStageRegisters* vs_regs, uint32_t shader_modifier)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < sizeof(VsStageRegisters) / 4 + 2);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
|
||||
|
||||
printf("\t vs_regs.m_spiShaderPgmLoVs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmLoVs);
|
||||
printf("\t vs_regs.m_spiShaderPgmHiVs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmHiVs);
|
||||
printf("\t vs_regs.m_spiShaderPgmRsrc1Vs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmRsrc1Vs);
|
||||
printf("\t vs_regs.m_spiShaderPgmRsrc2Vs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmRsrc2Vs);
|
||||
printf("\t vs_regs.m_spiVsOutConfig = %08" PRIx32 "\n", vs_regs->m_spiVsOutConfig);
|
||||
printf("\t vs_regs.m_spiShaderPosFormat = %08" PRIx32 "\n", vs_regs->m_spiShaderPosFormat);
|
||||
printf("\t vs_regs.m_paClVsOutCntl = %08" PRIx32 "\n", vs_regs->m_paClVsOutCntl);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_VS);
|
||||
cmd[1] = shader_modifier;
|
||||
memcpy(&cmd[2], vs_regs, sizeof(VsStageRegisters));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSetEmbeddedVsShader(uint32_t* cmd, uint64_t size, uint32_t id, uint32_t shader_modifier)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 3);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t id = %" PRIu32 "\n", id);
|
||||
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_VS_EMBEDDED);
|
||||
cmd[1] = shader_modifier;
|
||||
cmd[2] = id;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSetPsShader350(uint32_t* cmd, uint64_t size, const uint32_t* ps_regs)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < sizeof(PsStageRegisters) / 12 + 1);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
|
||||
printf("\t ps_regs.m_spiShaderPgmLoPs = %08" PRIx32 "\n", ps_regs[0]);
|
||||
printf("\t ps_regs.m_spiShaderPgmHiPs = %08" PRIx32 "\n", ps_regs[1]);
|
||||
printf("\t ps_regs.m_spiShaderPgmRsrc1Ps = %08" PRIx32 "\n", ps_regs[2]);
|
||||
printf("\t ps_regs.m_spiShaderPgmRsrc2Ps = %08" PRIx32 "\n", ps_regs[3]);
|
||||
printf("\t ps_regs.m_spiShaderZFormat = %08" PRIx32 "\n", ps_regs[4]);
|
||||
printf("\t ps_regs.m_spiShaderColFormat = %08" PRIx32 "\n", ps_regs[5]);
|
||||
printf("\t ps_regs.m_spiPsInputEna = %08" PRIx32 "\n", ps_regs[6]);
|
||||
printf("\t ps_regs.m_spiPsInputAddr = %08" PRIx32 "\n", ps_regs[7]);
|
||||
printf("\t ps_regs.m_spiPsInControl = %08" PRIx32 "\n", ps_regs[8]);
|
||||
printf("\t ps_regs.m_spiBarycCntl = %08" PRIx32 "\n", ps_regs[9]);
|
||||
printf("\t ps_regs.m_dbShaderControl = %08" PRIx32 "\n", ps_regs[10]);
|
||||
printf("\t ps_regs.m_cbShaderMask = %08" PRIx32 "\n", ps_regs[11]);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_PS);
|
||||
memcpy(&cmd[1], ps_regs, 12 * 4);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSetCsShaderWithModifier(uint32_t* cmd, uint64_t size, const uint32_t* cs_regs, uint32_t shader_modifier)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 7 + 2);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
|
||||
|
||||
printf("\t cs_regs.m_computePgmLo = %08" PRIx32 "\n", cs_regs[0]);
|
||||
printf("\t cs_regs.m_computePgmHi = %08" PRIx32 "\n", cs_regs[1]);
|
||||
printf("\t cs_regs.m_computePgmRsrc1 = %08" PRIx32 "\n", cs_regs[2]);
|
||||
printf("\t cs_regs.m_computePgmRsrc2 = %08" PRIx32 "\n", cs_regs[3]);
|
||||
printf("\t cs_regs.m_computeNumThreadX = %08" PRIx32 "\n", cs_regs[4]);
|
||||
printf("\t cs_regs.m_computeNumThreadY = %08" PRIx32 "\n", cs_regs[5]);
|
||||
printf("\t cs_regs.m_computeNumThreadZ = %08" PRIx32 "\n", cs_regs[6]);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_CS);
|
||||
cmd[1] = shader_modifier;
|
||||
memcpy(&cmd[2], cs_regs, 7 * 4);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsDrawIndex(uint32_t* cmd, uint64_t size, uint32_t index_count, const void* index_addr, uint32_t flags,
|
||||
uint32_t type)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 6);
|
||||
|
||||
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\tsize = %" PRIu64 "\n", size);
|
||||
printf("\tindex_count = %" PRIu32 "\n", index_count);
|
||||
printf("\tindex_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(index_addr));
|
||||
printf("\tflags = %08" PRIx32 "\n", flags);
|
||||
printf("\ttype = %" PRIu32 "\n", type);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DRAW_INDEX);
|
||||
cmd[1] = index_count;
|
||||
cmd[2] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(index_addr) & 0xffffffffu);
|
||||
cmd[3] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(index_addr) >> 32u) & 0xffffffffu);
|
||||
cmd[4] = flags;
|
||||
cmd[5] = type;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsDrawIndexAuto(uint32_t* cmd, uint64_t size, uint32_t index_count, uint32_t flags)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 3);
|
||||
|
||||
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\tsize = %" PRIu64 "\n", size);
|
||||
printf("\tindex_count = %" PRIu32 "\n", index_count);
|
||||
printf("\tflags = %08" PRIx32 "\n", flags);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DRAW_INDEX_AUTO);
|
||||
cmd[1] = index_count;
|
||||
cmd[2] = flags;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsInsertWaitFlipDone(uint32_t* cmd, uint64_t size, uint32_t video_out_handle, uint32_t display_buffer_index)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 3);
|
||||
|
||||
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\tsize = %" PRIu64 "\n", size);
|
||||
printf("\tvideo_out_handle = %" PRIu32 "\n", video_out_handle);
|
||||
printf("\tdisplay_buffer_index = %" PRIu32 "\n", display_buffer_index);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_WAIT_FLIP_DONE);
|
||||
cmd[1] = video_out_handle;
|
||||
cmd[2] = display_buffer_index;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsDispatchDirect(uint32_t* cmd, uint64_t size, uint32_t thread_group_x, uint32_t thread_group_y,
|
||||
uint32_t thread_group_z, uint32_t mode)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 5);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t thread_group_x = %" PRIu32 "\n", thread_group_x);
|
||||
printf("\t thread_group_y = %" PRIu32 "\n", thread_group_y);
|
||||
printf("\t thread_group_z = %" PRIu32 "\n", thread_group_z);
|
||||
printf("\t mode = %" PRIu32 "\n", mode);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DISPATCH_DIRECT);
|
||||
cmd[1] = thread_group_x;
|
||||
cmd[2] = thread_group_y;
|
||||
cmd[3] = thread_group_z;
|
||||
cmd[4] = mode;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint32_t KYTY_SYSV_ABI GraphicsDrawInitDefaultHardwareState350(uint32_t* cmd, uint64_t size)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 2);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
|
||||
cmd[0] = KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_DRAW_RESET);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint32_t KYTY_SYSV_ABI GraphicsDispatchInitDefaultHardwareState(uint32_t* cmd, uint64_t size)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 2);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
|
||||
cmd[0] = KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_DISPATCH_RESET);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void dbg_dump_dcb(const char* type, uint32_t num_dw, uint32_t* cmd_buffer)
|
||||
{
|
||||
EXIT_IF(type == nullptr);
|
||||
|
||||
static int id = 0;
|
||||
|
||||
if (Config::CommandBufferDumpEnabled() && num_dw > 0 && cmd_buffer != nullptr)
|
||||
{
|
||||
Core::File f;
|
||||
String file_name = Config::GetCommandBufferDumpFolder().FixDirectorySlash() +
|
||||
String::FromPrintf("%04d_%04d_buffer_%s.log", GraphicsRunGetFrameNum(), id++, type);
|
||||
Core::File::CreateDirectories(file_name.DirectoryWithoutFilename());
|
||||
f.Create(file_name);
|
||||
if (f.IsInvalid())
|
||||
{
|
||||
printf(FG_BRIGHT_RED "Can't create file: %s\n" FG_DEFAULT, file_name.C_Str());
|
||||
return;
|
||||
}
|
||||
Pm4::DumpPm4PacketStream(&f, cmd_buffer, 0, num_dw);
|
||||
f.Close();
|
||||
}
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
|
||||
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(count != 1);
|
||||
|
||||
auto* dcb = (dcb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(dcb_gpu_addrs[0]));
|
||||
auto dcb_size = (dcb_sizes_in_bytes == nullptr ? 0 : dcb_sizes_in_bytes[0] / 4);
|
||||
auto* ccb = (ccb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(ccb_gpu_addrs[0]));
|
||||
auto ccb_size = (ccb_sizes_in_bytes == nullptr ? 0 : ccb_sizes_in_bytes[0] / 4);
|
||||
|
||||
dbg_dump_dcb("d", dcb_size, dcb);
|
||||
dbg_dump_dcb("c", ccb_size, ccb);
|
||||
|
||||
GraphicsRunSubmit(dcb, dcb_size, ccb, ccb_size);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
|
||||
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes, int handle, int index,
|
||||
int flip_mode, int64_t flip_arg)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(count != 1);
|
||||
|
||||
auto* dcb = (dcb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(dcb_gpu_addrs[0]));
|
||||
auto dcb_size = (dcb_sizes_in_bytes == nullptr ? 0 : dcb_sizes_in_bytes[0] / 4);
|
||||
auto* ccb = (ccb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(ccb_gpu_addrs[0]));
|
||||
auto ccb_size = (ccb_sizes_in_bytes == nullptr ? 0 : ccb_sizes_in_bytes[0] / 4);
|
||||
|
||||
dbg_dump_dcb("d", dcb_size, dcb);
|
||||
dbg_dump_dcb("c", ccb_size, ccb);
|
||||
|
||||
printf("\t handle = %" PRId32 "\n", handle);
|
||||
printf("\t index = %" PRId32 "\n", index);
|
||||
printf("\t flip_mode = %" PRId32 "\n", flip_mode);
|
||||
printf("\t flip_arg = %" PRId64 "\n", flip_arg);
|
||||
|
||||
GraphicsRunSubmitAndFlip(dcb, dcb_size, ccb, ccb_size, handle, index, flip_mode, flip_arg);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSubmitDone()
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
GraphicsRunDone();
|
||||
// GpuMemoryFrameDone();
|
||||
// GpuMemoryDbgDump();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void KYTY_SYSV_ABI GraphicsFlushMemory()
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
GraphicsRunDone();
|
||||
|
||||
EXIT("1");
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsAddEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id, void* udata)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return LibKernel::KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
return GraphicsRenderAddEqEvent(eq, id, udata);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsDeleteEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return LibKernel::KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
return GraphicsRenderDeleteEqEvent(eq, id);
|
||||
}
|
||||
|
||||
uint32_t KYTY_SYSV_ABI GraphicsMapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw,
|
||||
uint32_t* read_ptr_addr)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t pipe_id = %" PRIu32 "\n", pipe_id);
|
||||
printf("\t queue_id = %" PRIu32 "\n", queue_id);
|
||||
printf("\t ring_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(ring_addr));
|
||||
printf("\t ring_size_dw = %" PRIu32 "\n", ring_size_dw);
|
||||
printf("\t read_ptr_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(read_ptr_addr));
|
||||
|
||||
uint32_t id = GraphicsRunMapComputeQueue(pipe_id, queue_id, ring_addr, ring_size_dw, read_ptr_addr);
|
||||
|
||||
printf("\t queue = %" PRIu32 "\n", id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void KYTY_SYSV_ABI GraphicsUnmapComputeQueue(uint32_t id)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t id = %" PRIu32 "\n", id);
|
||||
|
||||
GraphicsRunUnmapComputeQueue(id);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsComputeWaitOnAddress(uint32_t* cmd, uint64_t size, uint32_t* gpu_addr, uint32_t mask, uint32_t func, uint32_t ref)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 6);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t gpu_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(gpu_addr));
|
||||
printf("\t mask = %08" PRIx32 "\n", mask);
|
||||
printf("\t func = %" PRIu32 "\n", func);
|
||||
printf("\t ref = %08" PRIx32 "\n", ref);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DISPATCH_WAIT_MEM);
|
||||
cmd[1] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(gpu_addr) & 0xffffffffu);
|
||||
cmd[2] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(gpu_addr) >> 32u) & 0xffffffffu);
|
||||
cmd[3] = mask;
|
||||
cmd[4] = func;
|
||||
cmd[5] = ref;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void KYTY_SYSV_ABI GraphicsDingDong(uint32_t ring_id, uint32_t offset_dw)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t ring_id = %" PRIu32 "\n", ring_id);
|
||||
printf("\t offset_dw = %" PRIu32 "\n", offset_dw);
|
||||
|
||||
GraphicsRunDingDong(ring_id, offset_dw);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsInsertPushMarker(uint32_t* cmd, uint64_t size, const char* str)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
auto len = strlen(str) + 1;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size * 4 < len + 1);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
printf("\t str = %s\n", str);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_PUSH_MARKER);
|
||||
|
||||
memcpy(cmd + 1, str, len);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsInsertPopMarker(uint32_t* cmd, uint64_t size)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(size < 2);
|
||||
|
||||
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
|
||||
printf("\t size = %" PRIu64 "\n", size);
|
||||
|
||||
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_POP_MARKER);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI GraphicsGetGpuCoreClockFrequency()
|
||||
{
|
||||
return LibKernel::KernelGetTscFrequency();
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsRegisterOwner(uint32_t* owner_handle, const char* name)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(owner_handle == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(name == nullptr);
|
||||
|
||||
printf("\t RegisterOwner: %s\n", name);
|
||||
|
||||
GpuMemoryRegisterOwner(owner_handle, name);
|
||||
|
||||
printf("\t handler: %" PRIu32 "\n", *owner_handle);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsRegisterResource(uint32_t* resource_handle, uint32_t owner_handle, const void* memory, size_t size,
|
||||
const char* name, uint32_t type, uint64_t user_data)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(resource_handle == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(memory == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(name == nullptr);
|
||||
|
||||
printf("\t RegisterResource: %s\n", name);
|
||||
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
|
||||
printf("\t addr: %016" PRIx64 "\n", reinterpret_cast<uint64_t>(memory));
|
||||
printf("\t size: %" PRIu64 "\n", size);
|
||||
printf("\t type: %" PRIu32 "\n", type);
|
||||
printf("\t user_data: %" PRIu64 "\n", user_data);
|
||||
|
||||
uint32_t rhandle = 0;
|
||||
|
||||
GpuMemoryRegisterResource(&rhandle, owner_handle, memory, size, name, type, user_data);
|
||||
|
||||
printf("\t handler: %" PRIu32 "\n", rhandle);
|
||||
|
||||
if (resource_handle != nullptr)
|
||||
{
|
||||
*resource_handle = rhandle;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterAllResourcesForOwner(uint32_t owner_handle)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
|
||||
|
||||
GpuMemoryUnregisterAllResourcesForOwner(owner_handle);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterOwnerAndResources(uint32_t owner_handle)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
|
||||
|
||||
GpuMemoryUnregisterOwnerAndResources(owner_handle);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterResource(uint32_t resource_handle)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t resource_handle: %" PRIu32 "\n", resource_handle);
|
||||
|
||||
GpuMemoryUnregisterResource(resource_handle);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
#include "Emulator/Graphics/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,349 @@
|
||||
#include "Emulator/Graphics/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 {
|
||||
|
||||
struct Label
|
||||
{
|
||||
VkDevice device = nullptr;
|
||||
VkEvent event = nullptr;
|
||||
bool active = false;
|
||||
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] = {};
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
for (auto& label: manager->m_labels)
|
||||
{
|
||||
if (label->active)
|
||||
{
|
||||
active_count++;
|
||||
|
||||
if (vkGetEventStatus(label->device, label->event) == VK_EVENT_SET)
|
||||
{
|
||||
label->active = false;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active_count == 0)
|
||||
{
|
||||
manager->m_cond_var.Wait(&manager->m_mutex);
|
||||
}
|
||||
|
||||
manager->m_mutex.Unlock();
|
||||
|
||||
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->active = false;
|
||||
label->dst_gpu_addr64 = dst_gpu_addr;
|
||||
label->value64 = value;
|
||||
label->dst_gpu_addr32 = nullptr;
|
||||
label->value32 = 0;
|
||||
label->event = nullptr;
|
||||
label->device = ctx->device;
|
||||
label->callback_1 = callback_1;
|
||||
label->callback_2 = callback_2;
|
||||
label->args[0] = args[0];
|
||||
label->args[1] = args[1];
|
||||
label->args[2] = args[2];
|
||||
label->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;
|
||||
}
|
||||
|
||||
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->active = false;
|
||||
label->dst_gpu_addr32 = dst_gpu_addr;
|
||||
label->value32 = value;
|
||||
label->dst_gpu_addr64 = nullptr;
|
||||
label->value64 = 0;
|
||||
label->event = nullptr;
|
||||
label->device = ctx->device;
|
||||
label->callback_1 = callback_1;
|
||||
label->callback_2 = callback_2;
|
||||
label->args[0] = args[0];
|
||||
label->args[1] = args[1];
|
||||
label->args[2] = args[2];
|
||||
label->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));
|
||||
|
||||
m_labels.RemoveAt(index);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(label->active);
|
||||
|
||||
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->active);
|
||||
|
||||
label->active = true;
|
||||
|
||||
EXIT_IF(label->event == nullptr);
|
||||
|
||||
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,150 @@
|
||||
#include "Emulator/Graphics/Pm4.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/File.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics::Pm4 {
|
||||
|
||||
static const char* g_names[256] = {};
|
||||
static const char* g_r_names[64] = {};
|
||||
static bool g_names_initialized = false;
|
||||
|
||||
static void init_names()
|
||||
{
|
||||
if (!g_names_initialized)
|
||||
{
|
||||
for (auto& n: g_names)
|
||||
{
|
||||
n = "<unknown>";
|
||||
}
|
||||
|
||||
for (auto& n: g_r_names)
|
||||
{
|
||||
n = "<unknown>";
|
||||
}
|
||||
|
||||
g_r_names[R_ZERO] = "R_ZERO";
|
||||
g_r_names[R_VS] = "R_VS";
|
||||
g_r_names[R_PS] = "R_PS";
|
||||
g_r_names[R_DRAW_INDEX] = "R_DRAW_INDEX";
|
||||
g_r_names[R_DRAW_INDEX_AUTO] = "R_DRAW_INDEX_AUTO";
|
||||
g_r_names[R_DRAW_RESET] = "R_DRAW_RESET";
|
||||
g_r_names[R_WAIT_FLIP_DONE] = "R_WAIT_FLIP_DONE";
|
||||
g_r_names[R_CS] = "R_CS";
|
||||
g_r_names[R_DISPATCH_DIRECT] = "R_DISPATCH_DIRECT";
|
||||
g_r_names[R_DISPATCH_RESET] = "R_DISPATCH_RESET";
|
||||
g_r_names[R_DISPATCH_WAIT_MEM] = "R_DISPATCH_WAIT_MEM";
|
||||
g_r_names[R_PUSH_MARKER] = "R_PUSH_MARKER";
|
||||
g_r_names[R_POP_MARKER] = "R_POP_MARKER";
|
||||
g_r_names[R_VS_EMBEDDED] = "R_VS_EMBEDDED";
|
||||
|
||||
g_names[IT_NOP] = "IT_NOP";
|
||||
g_names[IT_SET_BASE] = "IT_SET_BASE";
|
||||
g_names[IT_CLEAR_STATE] = "IT_CLEAR_STATE";
|
||||
g_names[IT_INDEX_BUFFER_SIZE] = "IT_INDEX_BUFFER_SIZE";
|
||||
g_names[IT_DISPATCH_DIRECT] = "IT_DISPATCH_DIRECT";
|
||||
g_names[IT_DISPATCH_INDIRECT] = "IT_DISPATCH_INDIRECT";
|
||||
g_names[IT_SET_PREDICATION] = "IT_SET_PREDICATION";
|
||||
g_names[IT_COND_EXEC] = "IT_COND_EXEC";
|
||||
g_names[IT_DRAW_INDIRECT] = "IT_DRAW_INDIRECT";
|
||||
g_names[IT_DRAW_INDEX_INDIRECT] = "IT_DRAW_INDEX_INDIRECT";
|
||||
g_names[IT_INDEX_BASE] = "IT_INDEX_BASE";
|
||||
g_names[IT_DRAW_INDEX_2] = "IT_DRAW_INDEX_2";
|
||||
g_names[IT_CONTEXT_CONTROL] = "IT_CONTEXT_CONTROL";
|
||||
g_names[IT_INDEX_TYPE] = "IT_INDEX_TYPE";
|
||||
g_names[IT_DRAW_INDIRECT_MULTI] = "IT_DRAW_INDIRECT_MULTI";
|
||||
g_names[IT_DRAW_INDEX_AUTO] = "IT_DRAW_INDEX_AUTO";
|
||||
g_names[IT_NUM_INSTANCES] = "IT_NUM_INSTANCES";
|
||||
g_names[IT_INDIRECT_BUFFER_CNST] = "IT_INDIRECT_BUFFER_CNST";
|
||||
g_names[IT_DRAW_INDEX_OFFSET_2] = "IT_DRAW_INDEX_OFFSET_2";
|
||||
g_names[IT_WRITE_DATA] = "IT_WRITE_DATA";
|
||||
g_names[IT_MEM_SEMAPHORE] = "IT_MEM_SEMAPHORE";
|
||||
g_names[IT_DRAW_INDEX_INDIRECT_MULTI] = "IT_DRAW_INDEX_INDIRECT_MULTI";
|
||||
g_names[IT_WAIT_REG_MEM] = "IT_WAIT_REG_MEM";
|
||||
g_names[IT_INDIRECT_BUFFER] = "IT_INDIRECT_BUFFER";
|
||||
g_names[IT_COPY_DATA] = "IT_COPY_DATA";
|
||||
g_names[IT_CP_DMA] = "IT_CP_DMA";
|
||||
g_names[IT_PFP_SYNC_ME] = "IT_PFP_SYNC_ME";
|
||||
g_names[IT_SURFACE_SYNC] = "IT_SURFACE_SYNC";
|
||||
g_names[IT_EVENT_WRITE] = "IT_EVENT_WRITE";
|
||||
g_names[IT_EVENT_WRITE_EOP] = "IT_EVENT_WRITE_EOP";
|
||||
g_names[IT_EVENT_WRITE_EOS] = "IT_EVENT_WRITE_EOS";
|
||||
g_names[IT_RELEASE_MEM] = "IT_RELEASE_MEM";
|
||||
g_names[IT_DMA_DATA] = "IT_DMA_DATA";
|
||||
g_names[IT_ACQUIRE_MEM] = "IT_ACQUIRE_MEM";
|
||||
g_names[IT_REWIND] = "IT_REWIND";
|
||||
g_names[IT_SET_CONFIG_REG] = "IT_SET_CONFIG_REG";
|
||||
g_names[IT_SET_CONTEXT_REG] = "IT_SET_CONTEXT_REG";
|
||||
g_names[IT_SET_SH_REG] = "IT_SET_SH_REG";
|
||||
g_names[IT_SET_QUEUE_REG] = "IT_SET_QUEUE_REG";
|
||||
g_names[IT_SET_UCONFIG_REG] = "IT_SET_UCONFIG_REG";
|
||||
g_names[IT_WRITE_CONST_RAM] = "IT_WRITE_CONST_RAM";
|
||||
g_names[IT_DUMP_CONST_RAM] = "IT_DUMP_CONST_RAM";
|
||||
g_names[IT_INCREMENT_CE_COUNTER] = "IT_INCREMENT_CE_COUNTER";
|
||||
g_names[IT_INCREMENT_DE_COUNTER] = "IT_INCREMENT_DE_COUNTER";
|
||||
g_names[IT_WAIT_ON_CE_COUNTER] = "IT_WAIT_ON_CE_COUNTER";
|
||||
g_names[IT_WAIT_ON_DE_COUNTER_DIFF] = "IT_WAIT_ON_DE_COUNTER_DIFF";
|
||||
g_names[IT_DISPATCH_DRAW_PREAMBLE] = "IT_DISPATCH_DRAW_PREAMBLE";
|
||||
g_names[IT_DISPATCH_DRAW] = "IT_DISPATCH_DRAW";
|
||||
|
||||
g_names_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DumpPm4PacketStream(Core::File* file, uint32_t* cmd_buffer, uint32_t start_dw, uint32_t num_dw)
|
||||
{
|
||||
init_names();
|
||||
|
||||
// db_dump();
|
||||
|
||||
file->Printf("----- Buffer --- dwords: 0x%05" PRIx32 ", offset : %u, addr: %016" PRIx64 " ----- \n", num_dw, start_dw,
|
||||
reinterpret_cast<uint64_t>(cmd_buffer));
|
||||
|
||||
auto* cmd = cmd_buffer + start_dw;
|
||||
auto dw = num_dw;
|
||||
for (;;)
|
||||
{
|
||||
if (dw == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(dw < 2);
|
||||
EXIT_NOT_IMPLEMENTED(dw > num_dw);
|
||||
|
||||
auto cmd_id = *cmd++;
|
||||
|
||||
file->Printf("%05" PRIx32 " | 0x%08" PRIx32 " | ", start_dw, cmd_id);
|
||||
|
||||
uint32_t len = 0;
|
||||
|
||||
if ((cmd_id & 0xC0000000u) == 0xC0000000u)
|
||||
{
|
||||
bool sh_gx = (cmd_id & 0x2u) == 0;
|
||||
len = ((cmd_id >> 16u) & 0x3fffu) + 1;
|
||||
uint8_t op = ((cmd_id >> 8u) & 0xffu);
|
||||
auto r = ((cmd_id >> 2u) & 0x3fu);
|
||||
|
||||
file->Printf("%s %s(OP:0x%02" PRIx8 ") SH:%s CNT:%u\n", g_names[op], (op == IT_NOP ? g_r_names[r] : ""), op,
|
||||
sh_gx ? "GX" : "CX", len);
|
||||
|
||||
for (uint32_t i = 0; i < len; i++)
|
||||
{
|
||||
file->Printf(" | 0x%08" PRIx32 " | \n", cmd[i]);
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf("?????\n");
|
||||
}
|
||||
|
||||
cmd += len;
|
||||
dw -= len + 1;
|
||||
start_dw += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics::Pm4
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
#include "Emulator/Graphics/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,244 @@
|
||||
#include "Emulator/Graphics/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;
|
||||
}
|
||||
|
||||
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];
|
||||
auto nfmt = params[PARAM_NFMT];
|
||||
auto width = params[PARAM_WIDTH];
|
||||
auto height = params[PARAM_HEIGHT];
|
||||
auto levels = params[PARAM_LEVELS];
|
||||
auto swizzle = params[PARAM_SWIZZLE];
|
||||
|
||||
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;
|
||||
|
||||
vk_obj->extent.width = width;
|
||||
vk_obj->extent.height = height;
|
||||
vk_obj->format = pixel_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 = levels;
|
||||
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 = VK_IMAGE_USAGE_TRANSFER_DST_BIT | 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;
|
||||
|
||||
// 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 = get_swizzle(swizzle & 0xffu);
|
||||
create_info.components.g = get_swizzle((swizzle >> 8u) & 0xffu);
|
||||
create_info.components.b = get_swizzle((swizzle >> 16u) & 0xffu);
|
||||
create_info.components.a = get_swizzle((swizzle >> 24u) & 0xffu);
|
||||
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);
|
||||
|
||||
bool tile = (params[TextureObject::PARAM_TILE] != 0);
|
||||
auto dfmt = params[TextureObject::PARAM_DFMT];
|
||||
auto nfmt = params[TextureObject::PARAM_NFMT];
|
||||
auto width = params[TextureObject::PARAM_WIDTH];
|
||||
auto height = params[TextureObject::PARAM_HEIGHT];
|
||||
auto levels = params[TextureObject::PARAM_LEVELS];
|
||||
bool neo = Config::IsNeo();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(levels >= 16);
|
||||
|
||||
uint32_t level_sizes[16];
|
||||
|
||||
TileGetTextureSize(dfmt, nfmt, width, height, 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;
|
||||
|
||||
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;
|
||||
|
||||
offset += level_sizes[i];
|
||||
|
||||
if (mip_width > 1)
|
||||
{
|
||||
mip_width /= 2;
|
||||
}
|
||||
if (mip_height > 1)
|
||||
{
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (tile)
|
||||
{
|
||||
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);
|
||||
delete[] temp_buf;
|
||||
} else
|
||||
{
|
||||
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size, regions);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureObject::Equal(const uint64_t* other) const
|
||||
{
|
||||
return (params[PARAM_DFMT] == other[PARAM_DFMT] && params[PARAM_NFMT] == other[PARAM_NFMT] &&
|
||||
params[PARAM_WIDTH] == other[PARAM_WIDTH] && params[PARAM_HEIGHT] == other[PARAM_HEIGHT] &&
|
||||
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,674 @@
|
||||
#include "Emulator/Graphics/Tile.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
|
||||
#include "Emulator/Graphics/AsyncJob.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#if KYTY_COMPILER != KYTY_COMPILER_CLANG
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct Uint128
|
||||
{
|
||||
uint64_t n[2];
|
||||
};
|
||||
|
||||
struct Uint256
|
||||
{
|
||||
Uint128 n[2];
|
||||
};
|
||||
|
||||
class Tiler
|
||||
{
|
||||
public:
|
||||
Tiler(): m_job1(nullptr), m_job2(nullptr) /*, m_job3(nullptr), m_job4(nullptr)*/
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
|
||||
}
|
||||
virtual ~Tiler() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(Tiler);
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
|
||||
AsyncJob m_job1;
|
||||
AsyncJob m_job2;
|
||||
// AsyncJob m_job3;
|
||||
// AsyncJob m_job4;
|
||||
};
|
||||
|
||||
class Tiler32
|
||||
{
|
||||
public:
|
||||
uint32_t m_macro_tile_height = 0;
|
||||
uint32_t m_bank_height = 0;
|
||||
uint32_t m_num_banks = 0;
|
||||
uint32_t m_num_pipes = 0;
|
||||
uint32_t m_padded_width = 0;
|
||||
uint32_t m_padded_height = 0;
|
||||
uint32_t m_pipe_bits = 0;
|
||||
uint32_t m_bank_bits = 0;
|
||||
|
||||
void Init(uint32_t width, uint32_t height, bool neo)
|
||||
{
|
||||
m_macro_tile_height = (neo ? 128 : 64);
|
||||
m_bank_height = neo ? 2 : 1;
|
||||
m_num_banks = neo ? 8 : 16;
|
||||
m_num_pipes = neo ? 16 : 8;
|
||||
m_padded_width = width;
|
||||
if (height == 1080)
|
||||
{
|
||||
m_padded_height = neo ? 1152 : 1088;
|
||||
}
|
||||
if (height == 720)
|
||||
{
|
||||
m_padded_height = 768;
|
||||
}
|
||||
m_pipe_bits = neo ? 4 : 3;
|
||||
m_bank_bits = neo ? 3 : 4;
|
||||
}
|
||||
|
||||
static uint32_t GetElementIndex(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t elem = 0;
|
||||
elem |= ((x >> 0u) & 0x1u) << 0u;
|
||||
elem |= ((x >> 1u) & 0x1u) << 1u;
|
||||
elem |= ((y >> 0u) & 0x1u) << 2u;
|
||||
elem |= ((x >> 2u) & 0x1u) << 3u;
|
||||
elem |= ((y >> 1u) & 0x1u) << 4u;
|
||||
elem |= ((y >> 2u) & 0x1u) << 5u;
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
static uint32_t GetPipeIndex(uint32_t x, uint32_t y, bool neo)
|
||||
{
|
||||
uint32_t pipe = 0;
|
||||
|
||||
if (!neo)
|
||||
{
|
||||
pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u;
|
||||
pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u;
|
||||
pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u;
|
||||
} else
|
||||
{
|
||||
pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u;
|
||||
pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u;
|
||||
pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u;
|
||||
pipe |= (((x >> 6u) ^ (y >> 5u)) & 0x1u) << 3u;
|
||||
}
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
static uint32_t IntLog2(uint32_t i)
|
||||
{
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
return 31 - __builtin_clz(i | 1u);
|
||||
#else
|
||||
unsigned long temp;
|
||||
_BitScanReverse(&temp, i | 1u);
|
||||
return temp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t GetBankIndex(uint32_t x, uint32_t y, uint32_t bank_width, uint32_t bank_height, uint32_t num_banks, uint32_t num_pipes)
|
||||
{
|
||||
const uint32_t x_shift_offset = IntLog2(bank_width * num_pipes);
|
||||
const uint32_t y_shift_offset = IntLog2(bank_height);
|
||||
const uint32_t xs = x >> x_shift_offset;
|
||||
const uint32_t ys = y >> y_shift_offset;
|
||||
uint32_t bank = 0;
|
||||
switch (num_banks)
|
||||
{
|
||||
case 8:
|
||||
bank |= (((xs >> 3u) ^ (ys >> 5u)) & 0x1u) << 0u;
|
||||
bank |= (((xs >> 4u) ^ (ys >> 4u) ^ (ys >> 5u)) & 0x1u) << 1u;
|
||||
bank |= (((xs >> 5u) ^ (ys >> 3u)) & 0x1u) << 2u;
|
||||
break;
|
||||
case 16:
|
||||
bank |= (((xs >> 3u) ^ (ys >> 6u)) & 0x1u) << 0u;
|
||||
bank |= (((xs >> 4u) ^ (ys >> 5u) ^ (ys >> 6u)) & 0x1u) << 1u;
|
||||
bank |= (((xs >> 5u) ^ (ys >> 4u)) & 0x1u) << 2u;
|
||||
bank |= (((xs >> 6u) ^ (ys >> 3u)) & 0x1u) << 3u;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
return bank;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y, bool neo) const
|
||||
{
|
||||
uint64_t element_index = GetElementIndex(x, y);
|
||||
|
||||
uint32_t xh = x;
|
||||
uint32_t yh = y;
|
||||
uint64_t pipe = GetPipeIndex(xh, yh, neo);
|
||||
uint64_t bank = GetBankIndex(xh, yh, 1, m_bank_height, m_num_banks, m_num_pipes);
|
||||
uint32_t tile_bytes = (8 * 8 * 32 + 7) / 8;
|
||||
uint64_t element_offset = (element_index * 32);
|
||||
uint64_t tile_split_slice = 0;
|
||||
|
||||
if (tile_bytes > 512)
|
||||
{
|
||||
tile_split_slice = element_offset / (512 * 8);
|
||||
element_offset %= (512 * 8);
|
||||
tile_bytes = 512;
|
||||
}
|
||||
|
||||
uint64_t macro_tile_bytes = (128 / 8) * (m_macro_tile_height / 8) * tile_bytes / (m_num_pipes * m_num_banks);
|
||||
uint64_t macro_tiles_per_row = m_padded_width / 128;
|
||||
uint64_t macro_tile_row_index = y / m_macro_tile_height;
|
||||
uint64_t macro_tile_column_index = x / 128;
|
||||
uint64_t macro_tile_index = (macro_tile_row_index * macro_tiles_per_row) + macro_tile_column_index;
|
||||
uint64_t macro_tile_offset = macro_tile_index * macro_tile_bytes;
|
||||
uint64_t macro_tiles_per_slice = macro_tiles_per_row * (m_padded_height / m_macro_tile_height);
|
||||
uint64_t slice_bytes = macro_tiles_per_slice * macro_tile_bytes;
|
||||
uint64_t slice_offset = tile_split_slice * slice_bytes;
|
||||
uint64_t tile_row_index = (y / 8) % m_bank_height;
|
||||
uint64_t tile_index = tile_row_index;
|
||||
uint64_t tile_offset = tile_index * tile_bytes;
|
||||
|
||||
uint64_t tile_split_slice_rotation = ((m_num_banks / 2) + 1) * tile_split_slice;
|
||||
bank ^= tile_split_slice_rotation;
|
||||
bank &= (m_num_banks - 1);
|
||||
|
||||
uint64_t total_offset = (slice_offset + macro_tile_offset + tile_offset) * 8 + element_offset;
|
||||
uint64_t bit_offset = total_offset & 0x7u;
|
||||
total_offset /= 8;
|
||||
|
||||
uint64_t pipe_interleave_offset = total_offset & 0xffu;
|
||||
uint64_t offset = total_offset >> 8u;
|
||||
uint64_t byte_offset =
|
||||
pipe_interleave_offset | (pipe << (8u)) | (bank << (8u + m_pipe_bits)) | (offset << (8u + m_pipe_bits + m_bank_bits));
|
||||
|
||||
return ((byte_offset << 3u) | bit_offset) / 8;
|
||||
}
|
||||
};
|
||||
|
||||
class Tiler1d
|
||||
{
|
||||
public:
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
uint32_t m_bits_per_element = 0;
|
||||
uint32_t m_tile_bytes = 0;
|
||||
uint32_t m_tiles_per_row = 0;
|
||||
|
||||
void Init(uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height, uint32_t padded_width, uint32_t /*padded_height*/,
|
||||
bool /*neo*/)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
if (nfmt == 9 && dfmt == 10)
|
||||
{
|
||||
// VK_FORMAT_R8G8B8A8_SRGB;
|
||||
m_bits_per_element = 32;
|
||||
} else if (nfmt == 9 && dfmt == 37)
|
||||
{
|
||||
// VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
m_bits_per_element = 128;
|
||||
m_width = std::max((m_width + 3) / 4, 1U);
|
||||
m_height = std::max((m_height + 3) / 4, 1U);
|
||||
} else
|
||||
{
|
||||
EXIT("unknown format: nfmt = %u, dfmt = %u\n", nfmt, dfmt);
|
||||
}
|
||||
|
||||
m_tile_bytes = (8 * 8 * 1 * m_bits_per_element + 7) / 8;
|
||||
m_tiles_per_row = padded_width / 8;
|
||||
}
|
||||
|
||||
static uint32_t GetElementIndex(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t elem = 0;
|
||||
elem |= ((x >> 0u) & 0x1u) << 0u;
|
||||
elem |= ((y >> 0u) & 0x1u) << 1u;
|
||||
elem |= ((x >> 1u) & 0x1u) << 2u;
|
||||
elem |= ((y >> 1u) & 0x1u) << 3u;
|
||||
elem |= ((x >> 2u) & 0x1u) << 4u;
|
||||
elem |= ((y >> 2u) & 0x1u) << 5u;
|
||||
return elem;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y, bool /*neo*/) const
|
||||
{
|
||||
uint64_t element_index = GetElementIndex(x, y);
|
||||
|
||||
uint64_t tile_row_index = y / 8;
|
||||
uint64_t tile_column_index = x / 8;
|
||||
uint64_t tile_offset = ((tile_row_index * m_tiles_per_row) + tile_column_index) * m_tile_bytes;
|
||||
uint64_t element_offset = element_index * m_bits_per_element;
|
||||
uint64_t offset = tile_offset * 8 + element_offset;
|
||||
return offset / 8;
|
||||
}
|
||||
};
|
||||
|
||||
static Tiler* g_tiler = nullptr;
|
||||
|
||||
void TileInit()
|
||||
{
|
||||
EXIT_IF(g_tiler != nullptr);
|
||||
|
||||
g_tiler = new Tiler;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-non-const-parameter)
|
||||
static void Detile32(const Tiler32* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
|
||||
{
|
||||
EXIT_IF(g_tiler == nullptr);
|
||||
|
||||
Core::LockGuard lock(g_tiler->m_mutex);
|
||||
|
||||
struct DetileParams
|
||||
{
|
||||
const Tiler32* t;
|
||||
uint32_t start_y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t dst_pitch;
|
||||
uint8_t* dst;
|
||||
const uint8_t* src;
|
||||
bool neo;
|
||||
};
|
||||
|
||||
auto func = [](void* args)
|
||||
{
|
||||
auto* p = static_cast<DetileParams*>(args);
|
||||
|
||||
auto* dst = p->dst;
|
||||
const auto* src = p->src;
|
||||
const Tiler32* t = p->t;
|
||||
uint32_t start_y = p->start_y;
|
||||
uint32_t width = p->width;
|
||||
uint32_t height = p->height;
|
||||
uint32_t dst_pitch = p->dst_pitch;
|
||||
bool neo = p->neo;
|
||||
|
||||
for (uint32_t y = start_y; y < height; y++)
|
||||
{
|
||||
uint32_t x = 0;
|
||||
uint64_t linear_offset = y * dst_pitch * 4;
|
||||
|
||||
for (; x + 1 < width; x += 2)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<uint64_t*>(dst + linear_offset) = *reinterpret_cast<const uint64_t*>(src + tiled_offset);
|
||||
linear_offset += 8;
|
||||
}
|
||||
if (x < width)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(dst + linear_offset) = *reinterpret_cast<const uint32_t*>(src + tiled_offset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DetileParams p1 {t, 0, width, height / 4, dst_pitch, dst, src, neo};
|
||||
DetileParams p2 {t, p1.height, width, /*(height * 2) / 4*/ height, dst_pitch, dst, src, neo};
|
||||
// DetileParams p3 {t, p2.height, width, (height * 3) / 4, dst_pitch, dst, src, neo};
|
||||
// DetileParams p4 {t, p3.height, width, height, dst_pitch, dst, src, neo};
|
||||
|
||||
g_tiler->m_job1.Execute(func, &p1);
|
||||
g_tiler->m_job2.Execute(func, &p2);
|
||||
// g_tiler->m_job3.Execute(func, &p3);
|
||||
// g_tiler->m_job4.Execute(func, &p4);
|
||||
|
||||
g_tiler->m_job1.Wait();
|
||||
g_tiler->m_job2.Wait();
|
||||
// g_tiler->m_job3.Wait();
|
||||
// g_tiler->m_job4.Wait();
|
||||
|
||||
// Core::Thread t1(func, &p1);
|
||||
// Core::Thread t2(func, &p2);
|
||||
// Core::Thread t3(func, &p3);
|
||||
// Core::Thread t4(func, &p4);
|
||||
//
|
||||
// t1.Join();
|
||||
// t2.Join();
|
||||
// t3.Join();
|
||||
// t4.Join();
|
||||
}
|
||||
|
||||
static void Detile32(const Tiler1d* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
|
||||
{
|
||||
for (uint32_t y = 0; y < height; y++)
|
||||
{
|
||||
uint32_t x = 0;
|
||||
uint64_t linear_offset = y * dst_pitch * 4;
|
||||
|
||||
for (; x + 1 < width; x += 2)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<uint64_t*>(dst + linear_offset) = *reinterpret_cast<const uint64_t*>(src + tiled_offset);
|
||||
linear_offset += 8;
|
||||
}
|
||||
if (x < width)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(dst + linear_offset) = *reinterpret_cast<const uint32_t*>(src + tiled_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Detile128(const Tiler1d* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
|
||||
{
|
||||
for (uint32_t y = 0; y < height; y++)
|
||||
{
|
||||
uint32_t x = 0;
|
||||
uint64_t linear_offset = y * dst_pitch * 16;
|
||||
|
||||
for (; x + 1 < width; x += 2)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<Uint256*>(dst + linear_offset) = *reinterpret_cast<const Uint256*>(src + tiled_offset);
|
||||
linear_offset += 32;
|
||||
}
|
||||
if (x < width)
|
||||
{
|
||||
auto tiled_offset = t->GetTiledOffset(x, y, neo);
|
||||
|
||||
*reinterpret_cast<Uint128*>(dst + linear_offset) = *reinterpret_cast<const Uint128*>(src + tiled_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Detile1d(const Tiler1d* t, uint8_t* dst, const uint8_t* src, bool neo)
|
||||
{
|
||||
if (t->m_bits_per_element == 32)
|
||||
{
|
||||
Detile32(t, t->m_width, t->m_height, t->m_width, dst, src, neo);
|
||||
} else if (t->m_bits_per_element == 128)
|
||||
{
|
||||
Detile128(t, t->m_width, t->m_height, t->m_width, dst, src, neo);
|
||||
} else
|
||||
{
|
||||
EXIT("Unknown size");
|
||||
}
|
||||
}
|
||||
|
||||
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t width, uint32_t height, bool neo)
|
||||
{
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(mode != TileMode::VideoOutTiled);
|
||||
|
||||
Tiler32 t;
|
||||
t.Init(width, height, neo);
|
||||
|
||||
Detile32(&t, width, height, width, static_cast<uint8_t*>(dst), static_cast<const uint8_t*>(src), neo);
|
||||
}
|
||||
|
||||
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height,
|
||||
uint32_t levels, bool neo)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(mode != TileMode::TextureTiled);
|
||||
|
||||
uint32_t padded_width[16] = {0};
|
||||
uint32_t padded_height[16] = {0};
|
||||
uint32_t level_sizes[16] = {0};
|
||||
|
||||
TileGetTextureSize(dfmt, nfmt, width, height, levels, true, neo, nullptr, level_sizes, padded_width, padded_height);
|
||||
|
||||
uint32_t mip_width = width;
|
||||
uint32_t mip_height = height;
|
||||
|
||||
auto* dstptr = static_cast<uint8_t*>(dst);
|
||||
const auto* srcptr = static_cast<const uint8_t*>(src);
|
||||
|
||||
for (int l = 0; l < levels; l++)
|
||||
{
|
||||
Tiler1d t;
|
||||
t.Init(dfmt, nfmt, mip_width, mip_height, padded_width[l], padded_height[l], neo);
|
||||
|
||||
Detile1d(&t, dstptr, srcptr, neo);
|
||||
|
||||
dstptr += level_sizes[l];
|
||||
srcptr += level_sizes[l];
|
||||
|
||||
if (mip_width > 1)
|
||||
{
|
||||
mip_width /= 2;
|
||||
}
|
||||
if (mip_height > 1)
|
||||
{
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileGetDepthSize(uint32_t width, uint32_t height, uint32_t z_format, uint32_t stencil_format, bool htile, bool neo,
|
||||
uint32_t* stencil_size, uint32_t* htile_size, uint32_t* depth_size, uint32_t* pitch)
|
||||
{
|
||||
struct SizeAlign
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t align;
|
||||
};
|
||||
|
||||
struct DepthInfo
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t z_format;
|
||||
uint32_t stencil_format;
|
||||
bool tile;
|
||||
bool neo;
|
||||
uint32_t pitch;
|
||||
SizeAlign stencil;
|
||||
SizeAlign htile;
|
||||
SizeAlign depth;
|
||||
};
|
||||
|
||||
static const DepthInfo infos_base[] = {
|
||||
{1920, 1080, 3, 0, true, false, 2048, {0, 0}, {196608, 2048}, {9437184, 32768}},
|
||||
{1920, 1080, 3, 0, false, false, 2048, {0, 0}, {0, 0}, {9437184, 32768}},
|
||||
{1280, 720, 3, 0, true, false, 1280, {0, 0}, {98304, 2048}, {3932160, 32768}},
|
||||
{1280, 720, 3, 0, false, false, 1280, {0, 0}, {0, 0}, {3932160, 32768}},
|
||||
{1920, 1080, 1, 0, true, false, 2048, {0, 0}, {196608, 2048}, {4718592, 32768}},
|
||||
{1920, 1080, 1, 0, false, false, 2048, {0, 0}, {0, 0}, {4718592, 32768}},
|
||||
{1280, 720, 1, 0, true, false, 1280, {0, 0}, {98304, 2048}, {1966080, 32768}},
|
||||
{1280, 720, 1, 0, false, false, 1280, {0, 0}, {0, 0}, {1966080, 32768}},
|
||||
{1920, 1080, 0, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {0, 0}},
|
||||
{1920, 1080, 0, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {0, 0}},
|
||||
{1280, 720, 0, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {0, 0}},
|
||||
{1280, 720, 0, 1, false, false, 1280, {983040, 32768}, {0, 0}, {0, 0}},
|
||||
{1920, 1080, 3, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {9437184, 32768}},
|
||||
{1920, 1080, 3, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {9437184, 32768}},
|
||||
{1280, 720, 3, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {3932160, 32768}},
|
||||
{1280, 720, 3, 1, false, false, 1280, {983040, 32768}, {0, 0}, {3932160, 32768}},
|
||||
{1920, 1080, 1, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {4718592, 32768}},
|
||||
{1920, 1080, 1, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {4718592, 32768}},
|
||||
{1280, 720, 1, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {1966080, 32768}},
|
||||
{1280, 720, 1, 1, false, false, 1280, {983040, 32768}, {0, 0}, {1966080, 32768}},
|
||||
};
|
||||
|
||||
static const DepthInfo infos_neo[] = {
|
||||
{1920, 1080, 3, 0, true, true, 1920, {0, 0}, {196608, 4096}, {8847360, 65536}},
|
||||
{1920, 1080, 3, 0, false, true, 1920, {0, 0}, {0, 0}, {8847360, 65536}},
|
||||
{1280, 720, 3, 0, true, true, 1280, {0, 0}, {131072, 4096}, {3932160, 65536}},
|
||||
{1280, 720, 3, 0, false, true, 1280, {0, 0}, {0, 0}, {3932160, 65536}},
|
||||
{1920, 1080, 1, 0, true, true, 2048, {0, 0}, {196608, 4096}, {4718592, 65536}},
|
||||
{1920, 1080, 1, 0, false, true, 2048, {0, 0}, {0, 0}, {4718592, 65536}},
|
||||
{1280, 720, 1, 0, true, true, 1280, {0, 0}, {131072, 4096}, {1966080, 65536}},
|
||||
{1280, 720, 1, 0, false, true, 1280, {0, 0}, {0, 0}, {1966080, 65536}},
|
||||
{1920, 1080, 0, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {0, 0}},
|
||||
{1920, 1080, 0, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {0, 0}},
|
||||
{1280, 720, 0, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {0, 0}},
|
||||
{1280, 720, 0, 1, false, true, 1280, {983040, 32768}, {0, 0}, {0, 0}},
|
||||
{1920, 1080, 3, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {9437184, 65536}},
|
||||
{1920, 1080, 3, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {9437184, 65536}},
|
||||
{1280, 720, 3, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {3932160, 65536}},
|
||||
{1280, 720, 3, 1, false, true, 1280, {983040, 32768}, {0, 0}, {3932160, 65536}},
|
||||
{1920, 1080, 1, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {4718592, 65536}},
|
||||
{1920, 1080, 1, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {4718592, 65536}},
|
||||
{1280, 720, 1, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {1966080, 65536}},
|
||||
{1280, 720, 1, 1, false, true, 1280, {983040, 32768}, {0, 0}, {1966080, 65536}},
|
||||
};
|
||||
|
||||
EXIT_IF(depth_size == nullptr);
|
||||
EXIT_IF(htile_size == nullptr);
|
||||
EXIT_IF(stencil_size == nullptr);
|
||||
EXIT_IF(pitch == nullptr);
|
||||
|
||||
if (neo)
|
||||
{
|
||||
for (const auto& i: infos_neo)
|
||||
{
|
||||
if (i.width == width && i.height == height && i.tile == htile && i.z_format == z_format && i.stencil_format == stencil_format)
|
||||
{
|
||||
*depth_size = i.depth.size;
|
||||
*htile_size = i.htile.size;
|
||||
*stencil_size = i.stencil.size;
|
||||
*pitch = i.pitch;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (const auto& i: infos_base)
|
||||
{
|
||||
if (i.width == width && i.height == height && i.tile == htile && i.z_format == z_format && i.stencil_format == stencil_format)
|
||||
{
|
||||
*depth_size = i.depth.size;
|
||||
*htile_size = i.htile.size;
|
||||
*stencil_size = i.stencil.size;
|
||||
*pitch = i.pitch;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*depth_size = 0;
|
||||
*htile_size = 0;
|
||||
*stencil_size = 0;
|
||||
}
|
||||
|
||||
void TileGetVideoOutSize(uint32_t width, uint32_t height, bool tile, bool neo, uint32_t* size)
|
||||
{
|
||||
EXIT_IF(size == nullptr);
|
||||
|
||||
if (width == 1920 && height == 1080 && tile && !neo)
|
||||
{
|
||||
*size = 8355840;
|
||||
}
|
||||
if (width == 1920 && height == 1080 && tile && neo)
|
||||
{
|
||||
*size = 8847360;
|
||||
}
|
||||
if (width == 1920 && height == 1080 && !tile && !neo)
|
||||
{
|
||||
*size = 8294400;
|
||||
}
|
||||
if (width == 1920 && height == 1080 && !tile && neo)
|
||||
{
|
||||
*size = 8294400;
|
||||
}
|
||||
if (width == 1280 && height == 720 && tile && !neo)
|
||||
{
|
||||
*size = 3932160;
|
||||
}
|
||||
if (width == 1280 && height == 720 && tile && neo)
|
||||
{
|
||||
*size = 3932160;
|
||||
}
|
||||
if (width == 1280 && height == 720 && !tile && !neo)
|
||||
{
|
||||
*size = 3686400;
|
||||
}
|
||||
if (width == 1280 && height == 720 && !tile && neo)
|
||||
{
|
||||
*size = 3686400;
|
||||
}
|
||||
}
|
||||
|
||||
void TileGetTextureSize(uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height, uint32_t levels, bool tile, bool neo,
|
||||
uint32_t* total_size, uint32_t* level_sizes, uint32_t* padded_width, uint32_t* padded_height)
|
||||
{
|
||||
struct Padded
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
struct TextureInfo
|
||||
{
|
||||
uint32_t dfmt;
|
||||
uint32_t nfmt;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t levels;
|
||||
bool tile;
|
||||
bool neo;
|
||||
uint32_t size[16];
|
||||
Padded padded[16];
|
||||
};
|
||||
|
||||
static const TextureInfo infos[] = {
|
||||
// clang-format off
|
||||
{ 10, 9, 512, 512, 10, false, false, {1048576, 262144, 65536, 16384, 4096, 1024, 512, 256, 256, 256, },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
|
||||
{ 10, 9, 512, 512, 10, false, true, {1048576, 262144, 65536, 16384, 4096, 1024, 512, 256, 256, 256, },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
|
||||
{ 10, 9, 512, 512, 10, true, false, {1048576, 262144, 65536, 16384, 4096, 1024, 256, 256, 256, 256, },
|
||||
{ {512, 512}, {256, 256}, {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
|
||||
{ 10, 9, 512, 512, 10, true, true, {1048576, 262144, 65536, 16384, 4096, 1024, 256, 256, 256, 256, },
|
||||
{ {512, 512}, {256, 256}, {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
|
||||
{ 37, 9, 512, 512, 10, false, false, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
|
||||
{ 37, 9, 512, 512, 10, false, true, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
|
||||
{ 37, 9, 512, 512, 10, true, false, {262144, 65536, 16384, 4096, 1024, 1024, 1024, 1024, 1024, 1024, },
|
||||
{ {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
|
||||
{ 37, 9, 512, 512, 10, true, true, {262144, 65536, 16384, 4096, 1024, 1024, 1024, 1024, 1024, 1024, },
|
||||
{ {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// EXIT_IF(total_size == nullptr);
|
||||
|
||||
for (const auto& i: infos)
|
||||
{
|
||||
if (i.dfmt == dfmt && i.nfmt == nfmt && i.width == width && i.height == height && i.levels >= levels && i.tile == tile &&
|
||||
i.neo == neo)
|
||||
{
|
||||
for (uint32_t l = 0; l < levels; l++)
|
||||
{
|
||||
if (total_size != nullptr)
|
||||
{
|
||||
*total_size += i.size[l];
|
||||
}
|
||||
if (level_sizes != nullptr)
|
||||
{
|
||||
level_sizes[l] = i.size[l];
|
||||
}
|
||||
if (padded_width != nullptr)
|
||||
{
|
||||
padded_width[l] = i.padded[l].width;
|
||||
}
|
||||
if (padded_height != nullptr)
|
||||
{
|
||||
padded_height[l] = i.padded[l].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,372 @@
|
||||
#include "Emulator/Graphics/Utils.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Graphics/GpuMemory.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 {
|
||||
|
||||
static void set_image_layout(VkCommandBuffer buffer, VkImage image, uint32_t levels, VkImageAspectFlags aspect_mask,
|
||||
VkImageLayout old_image_layout, VkImageLayout new_image_layout)
|
||||
{
|
||||
EXIT_IF(buffer == nullptr);
|
||||
|
||||
VkImageMemoryBarrier image_memory_barrier {};
|
||||
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
image_memory_barrier.pNext = nullptr;
|
||||
image_memory_barrier.srcAccessMask = 0;
|
||||
image_memory_barrier.dstAccessMask = 0;
|
||||
image_memory_barrier.oldLayout = old_image_layout;
|
||||
image_memory_barrier.newLayout = new_image_layout;
|
||||
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.image = image;
|
||||
image_memory_barrier.subresourceRange.aspectMask = aspect_mask;
|
||||
image_memory_barrier.subresourceRange.baseMipLevel = 0;
|
||||
image_memory_barrier.subresourceRange.levelCount = levels;
|
||||
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
|
||||
image_memory_barrier.subresourceRange.layerCount = 1;
|
||||
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.srcAccessMask = 0; // VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_TRANSFER_READ_BIT;
|
||||
}
|
||||
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.srcAccessMask = 0; // VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED)
|
||||
{
|
||||
image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.srcAccessMask = 0; /*VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT*/
|
||||
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
|
||||
{
|
||||
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(buffer, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||||
}
|
||||
|
||||
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, VideoOutVulkanImage* dst_image)
|
||||
{
|
||||
EXIT_IF(src_buffer == nullptr);
|
||||
EXIT_IF(src_buffer->buffer == nullptr);
|
||||
EXIT_IF(dst_image == nullptr);
|
||||
EXIT_IF(dst_image->image == nullptr);
|
||||
|
||||
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
|
||||
|
||||
set_image_layout(vk_buffer, dst_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkBufferImageCopy region {};
|
||||
region.bufferOffset = 0;
|
||||
region.bufferRowLength = 0;
|
||||
region.bufferImageHeight = 0;
|
||||
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.mipLevel = 0;
|
||||
region.imageSubresource.baseArrayLayer = 0;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
|
||||
region.imageOffset = {0, 0, 0};
|
||||
region.imageExtent = {dst_image->extent.width, dst_image->extent.height, 1};
|
||||
|
||||
vkCmdCopyBufferToImage(vk_buffer, src_buffer->buffer, dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
set_image_layout(vk_buffer, dst_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, TextureVulkanImage* dst_image,
|
||||
const Vector<BufferImageCopy>& regions)
|
||||
{
|
||||
EXIT_IF(src_buffer == nullptr);
|
||||
EXIT_IF(src_buffer->buffer == nullptr);
|
||||
EXIT_IF(dst_image == nullptr);
|
||||
EXIT_IF(dst_image->image == nullptr);
|
||||
|
||||
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(regions.Size() >= 16);
|
||||
|
||||
VkBufferImageCopy region[16];
|
||||
|
||||
uint32_t index = 0;
|
||||
for (const auto& r: regions)
|
||||
{
|
||||
region[index].bufferOffset = r.offset;
|
||||
region[index].bufferRowLength = 0;
|
||||
region[index].bufferImageHeight = 0;
|
||||
region[index].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region[index].imageSubresource.mipLevel = index;
|
||||
region[index].imageSubresource.baseArrayLayer = 0;
|
||||
region[index].imageSubresource.layerCount = 1;
|
||||
region[index].imageOffset = {0, 0, 0};
|
||||
region[index].imageExtent = {r.width, r.height, 1};
|
||||
index++;
|
||||
}
|
||||
|
||||
set_image_layout(vk_buffer, dst_image->image, index, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
vkCmdCopyBufferToImage(vk_buffer, src_buffer->buffer, dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, index, region);
|
||||
|
||||
set_image_layout(vk_buffer, dst_image->image, index, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
void UtilBlitImage(CommandBuffer* buffer, VideoOutVulkanImage* src_image, VulkanSwapchain* dst_swapchain)
|
||||
{
|
||||
EXIT_IF(src_image == nullptr);
|
||||
EXIT_IF(src_image->image == nullptr);
|
||||
EXIT_IF(dst_swapchain == nullptr);
|
||||
|
||||
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
|
||||
|
||||
auto* blt_dst_image = dst_swapchain->swapchain_images[dst_swapchain->current_index];
|
||||
|
||||
set_image_layout(vk_buffer, src_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
set_image_layout(vk_buffer, blt_dst_image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkImageBlit region {};
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.srcSubresource.mipLevel = 0;
|
||||
region.srcSubresource.baseArrayLayer = 0;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
region.srcOffsets[0].x = 0;
|
||||
region.srcOffsets[0].y = 0;
|
||||
region.srcOffsets[0].z = 0;
|
||||
region.srcOffsets[1].x = static_cast<int>(src_image->extent.width);
|
||||
region.srcOffsets[1].y = static_cast<int>(src_image->extent.height);
|
||||
region.srcOffsets[1].z = 1;
|
||||
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.dstSubresource.mipLevel = 0;
|
||||
region.dstSubresource.baseArrayLayer = 0;
|
||||
region.dstSubresource.layerCount = 1;
|
||||
region.dstOffsets[0].x = 0;
|
||||
region.dstOffsets[0].y = 0;
|
||||
region.dstOffsets[0].z = 0;
|
||||
region.dstOffsets[1].x = static_cast<int>(dst_swapchain->swapchain_extent.width);
|
||||
region.dstOffsets[1].y = static_cast<int>(dst_swapchain->swapchain_extent.height);
|
||||
region.dstOffsets[1].z = 1;
|
||||
|
||||
vkCmdBlitImage(vk_buffer, src_image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, blt_dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ®ion, VK_FILTER_LINEAR);
|
||||
|
||||
set_image_layout(vk_buffer, src_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
void VulkanCreateBuffer(GraphicContext* gctx, uint64_t size, VulkanBuffer* buffer)
|
||||
{
|
||||
EXIT_IF(gctx == nullptr);
|
||||
EXIT_IF(buffer == nullptr);
|
||||
EXIT_IF(buffer->buffer != nullptr);
|
||||
|
||||
VkBufferCreateInfo buffer_info {};
|
||||
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
buffer_info.size = size;
|
||||
buffer_info.usage = buffer->usage;
|
||||
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
vkCreateBuffer(gctx->device, &buffer_info, nullptr, &buffer->buffer);
|
||||
EXIT_NOT_IMPLEMENTED(buffer->buffer == nullptr);
|
||||
|
||||
vkGetBufferMemoryRequirements(gctx->device, buffer->buffer, &buffer->memory.requirements);
|
||||
|
||||
bool allocated = VulkanAllocate(gctx, &buffer->memory);
|
||||
EXIT_NOT_IMPLEMENTED(!allocated);
|
||||
|
||||
// vkBindBufferMemory(gctx->device, buffer->buffer, buffer->memory.memory, buffer->memory.offset);
|
||||
VulkanBindBufferMemory(gctx, buffer, &buffer->memory);
|
||||
}
|
||||
|
||||
void VulkanDeleteBuffer(GraphicContext* gctx, VulkanBuffer* buffer)
|
||||
{
|
||||
EXIT_IF(buffer == nullptr);
|
||||
EXIT_IF(gctx == nullptr);
|
||||
|
||||
DeleteDescriptor(buffer);
|
||||
|
||||
vkDestroyBuffer(gctx->device, buffer->buffer, nullptr);
|
||||
VulkanFree(gctx, &buffer->memory);
|
||||
buffer->buffer = nullptr;
|
||||
}
|
||||
|
||||
void UtilFillImage(GraphicContext* ctx, VideoOutVulkanImage* image, const void* src_data, uint64_t size)
|
||||
{
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(image == 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);
|
||||
|
||||
void* data = nullptr;
|
||||
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, size, 0, &data);
|
||||
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
|
||||
std::memcpy(data, src_data, size);
|
||||
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
|
||||
VulkanUnmapMemory(ctx, &staging_buffer.memory);
|
||||
|
||||
CommandBuffer buffer;
|
||||
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
|
||||
|
||||
buffer.Begin();
|
||||
UtilBufferToImage(&buffer, &staging_buffer, image);
|
||||
buffer.End();
|
||||
buffer.Execute();
|
||||
buffer.WaitForFence();
|
||||
|
||||
VulkanDeleteBuffer(ctx, &staging_buffer);
|
||||
}
|
||||
|
||||
void UtilSetImageLayoutOptimal(DepthStencilVulkanImage* image)
|
||||
{
|
||||
CommandBuffer buffer;
|
||||
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
|
||||
|
||||
buffer.Begin();
|
||||
|
||||
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
|
||||
|
||||
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
if (image->format == VK_FORMAT_D24_UNORM_S8_UINT || image->format == VK_FORMAT_D32_SFLOAT_S8_UINT)
|
||||
{
|
||||
aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
|
||||
set_image_layout(vk_buffer, image->image, 1, aspect_mask, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
|
||||
buffer.End();
|
||||
buffer.Execute();
|
||||
buffer.WaitForFence();
|
||||
}
|
||||
|
||||
void UtilSetImageLayoutOptimal(VideoOutVulkanImage* image)
|
||||
{
|
||||
CommandBuffer buffer;
|
||||
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
|
||||
|
||||
buffer.Begin();
|
||||
|
||||
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
|
||||
|
||||
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
set_image_layout(vk_buffer, image->image, 1, aspect_mask, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
buffer.End();
|
||||
buffer.Execute();
|
||||
buffer.WaitForFence();
|
||||
}
|
||||
|
||||
void UtilFillImage(GraphicContext* ctx, TextureVulkanImage* image, const void* src_data, uint64_t size,
|
||||
const Vector<BufferImageCopy>& regions)
|
||||
{
|
||||
EXIT_IF(ctx == nullptr);
|
||||
EXIT_IF(image == 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);
|
||||
|
||||
void* data = nullptr;
|
||||
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
|
||||
std::memcpy(data, src_data, size);
|
||||
VulkanUnmapMemory(ctx, &staging_buffer.memory);
|
||||
|
||||
CommandBuffer buffer;
|
||||
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
|
||||
|
||||
buffer.Begin();
|
||||
UtilBufferToImage(&buffer, &staging_buffer, image, regions);
|
||||
buffer.End();
|
||||
buffer.Execute();
|
||||
buffer.WaitForFence();
|
||||
|
||||
VulkanDeleteBuffer(ctx, &staging_buffer);
|
||||
}
|
||||
|
||||
void UtilCopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size)
|
||||
{
|
||||
EXIT_IF(src_buffer == nullptr);
|
||||
EXIT_IF(src_buffer->buffer == nullptr);
|
||||
EXIT_IF(dst_buffer == nullptr);
|
||||
EXIT_IF(dst_buffer->buffer == nullptr);
|
||||
|
||||
CommandBuffer buffer;
|
||||
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
|
||||
|
||||
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
|
||||
|
||||
buffer.Begin();
|
||||
|
||||
VkBufferCopy copy_region {};
|
||||
copy_region.srcOffset = 0;
|
||||
copy_region.dstOffset = 0;
|
||||
copy_region.size = size;
|
||||
|
||||
vkCmdCopyBuffer(vk_buffer, src_buffer->buffer, dst_buffer->buffer, 1, ©_region);
|
||||
|
||||
buffer.End();
|
||||
buffer.Execute();
|
||||
buffer.WaitForFence();
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "Emulator/Graphics/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,722 @@
|
||||
#include "Emulator/Graphics/VideoOut.h"
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/LinkList.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Config.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
#include "Emulator/Graphics/GraphicsRender.h"
|
||||
#include "Emulator/Graphics/Tile.h"
|
||||
#include "Emulator/Graphics/VideoOutBuffer.h"
|
||||
#include "Emulator/Graphics/Window.h"
|
||||
#include "Emulator/Kernel/Pthread.h"
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
struct GraphicContext;
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
namespace Kyty::Libs::VideoOut {
|
||||
|
||||
LIB_NAME("VideoOut", "VideoOut");
|
||||
|
||||
namespace EventQueue = LibKernel::EventQueue;
|
||||
|
||||
constexpr int VIDEO_OUT_EVENT_FLIP = 0;
|
||||
|
||||
struct VideoOutResolutionStatus
|
||||
{
|
||||
uint32_t fullWidth = 1280;
|
||||
uint32_t fullHeight = 720;
|
||||
uint32_t paneWidth = 1280;
|
||||
uint32_t paneHeight = 720;
|
||||
uint64_t refreshRate = 3;
|
||||
float screenSizeInInch = 50;
|
||||
uint16_t flags = 0;
|
||||
uint16_t reserved0 = 0;
|
||||
uint32_t reserved1[3] = {0};
|
||||
};
|
||||
|
||||
struct VideoOutBufferAttribute
|
||||
{
|
||||
uint32_t pixelFormat;
|
||||
uint32_t tilingMode;
|
||||
uint32_t aspectRatio;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t pitchInPixel;
|
||||
uint32_t option;
|
||||
uint32_t reserved0;
|
||||
uint64_t reserved1;
|
||||
};
|
||||
|
||||
struct VideoOutFlipStatus
|
||||
{
|
||||
uint64_t count = 0;
|
||||
uint64_t processTime = 0;
|
||||
uint64_t tsc = 0;
|
||||
int64_t flipArg = 0;
|
||||
uint64_t submitTsc = 0;
|
||||
uint64_t reserved0 = 0;
|
||||
int32_t gcQueueNum = 0;
|
||||
int32_t flipPendingNum = 0;
|
||||
int32_t currentBuffer = 0;
|
||||
uint32_t reserved1 = 0;
|
||||
};
|
||||
|
||||
struct VideoOutBufferSet
|
||||
{
|
||||
VideoOutBufferAttribute attr = {};
|
||||
int start_index = 0;
|
||||
int num = 0;
|
||||
};
|
||||
|
||||
struct VideoOutBufferInfo
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
Graphics::VideoOutVulkanImage* buffer_vulkan = nullptr;
|
||||
uint64_t buffer_size = 0;
|
||||
int set_id = 0;
|
||||
};
|
||||
|
||||
struct VideoOutConfig
|
||||
{
|
||||
VideoOutResolutionStatus resolution;
|
||||
bool opened = false;
|
||||
int flip_rate = 0;
|
||||
EventQueue::KernelEqueue flip_eq = nullptr;
|
||||
VideoOutFlipStatus flip_status;
|
||||
VideoOutBufferInfo buffers[16];
|
||||
VideoOutBufferSet buffers_sets[16];
|
||||
int buffers_sets_num = 0;
|
||||
};
|
||||
|
||||
class FlipQueue
|
||||
{
|
||||
public:
|
||||
FlipQueue() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~FlipQueue() { KYTY_NOT_IMPLEMENTED; }
|
||||
KYTY_CLASS_NO_COPY(FlipQueue);
|
||||
|
||||
bool Submit(VideoOutConfig* cfg, int index, int64_t flip_arg);
|
||||
bool Flip(uint32_t micros);
|
||||
void GetFlipStatus(VideoOutConfig* cfg, VideoOutFlipStatus* out);
|
||||
void Wait(VideoOutConfig* cfg, int index);
|
||||
|
||||
private:
|
||||
struct Request
|
||||
{
|
||||
VideoOutConfig* cfg;
|
||||
int index;
|
||||
int64_t flip_arg;
|
||||
uint64_t submit_tsc;
|
||||
};
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
Core::CondVar m_submit_cond_var;
|
||||
Core::CondVar m_done_cond_var;
|
||||
Core::List<Request> m_requests;
|
||||
};
|
||||
|
||||
class VideoOutContext
|
||||
{
|
||||
public:
|
||||
static constexpr int VIDEO_OUT_NUM_MAX = 2;
|
||||
|
||||
VideoOutContext() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~VideoOutContext() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(VideoOutContext);
|
||||
|
||||
int Open();
|
||||
void Close(int handle);
|
||||
VideoOutConfig* Get(int handle);
|
||||
|
||||
VideoOutBufferImageInfo FindImage(void* buffer);
|
||||
|
||||
void Init(uint32_t width, uint32_t height);
|
||||
|
||||
Graphics::GraphicContext* GetGraphicCtx()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
if (m_graphic_ctx == nullptr)
|
||||
{
|
||||
m_graphic_ctx = Graphics::WindowGetGraphicContext();
|
||||
}
|
||||
|
||||
return m_graphic_ctx;
|
||||
}
|
||||
|
||||
FlipQueue& GetFlipQueue() { return m_flip_queue; }
|
||||
|
||||
private:
|
||||
Core::Mutex m_mutex;
|
||||
VideoOutConfig m_video_out_ctx[VIDEO_OUT_NUM_MAX];
|
||||
Graphics::GraphicContext* m_graphic_ctx = nullptr;
|
||||
FlipQueue m_flip_queue;
|
||||
};
|
||||
|
||||
static VideoOutContext* g_video_out_context = nullptr;
|
||||
|
||||
static uint64_t calc_buffer_size(const VideoOutBufferAttribute* attribute)
|
||||
{
|
||||
bool tile = attribute->tilingMode == 0;
|
||||
bool neo = Config::IsNeo();
|
||||
uint32_t width = attribute->width;
|
||||
uint32_t height = attribute->height;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(attribute->width != attribute->pitchInPixel);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->option != 0);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->aspectRatio != 0);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->pixelFormat != 0x80000000);
|
||||
|
||||
uint32_t size = 0;
|
||||
Graphics::TileGetVideoOutSize(width, height, tile, neo, &size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void VideoOutInit(uint32_t width, uint32_t height)
|
||||
{
|
||||
EXIT_IF(g_video_out_context != nullptr);
|
||||
|
||||
g_video_out_context = new VideoOutContext;
|
||||
|
||||
g_video_out_context->Init(width, height);
|
||||
}
|
||||
|
||||
void VideoOutContext::Init(uint32_t width, uint32_t height)
|
||||
{
|
||||
for (auto& ctx: m_video_out_ctx)
|
||||
{
|
||||
ctx.resolution.fullWidth = width;
|
||||
ctx.resolution.fullHeight = height;
|
||||
ctx.resolution.paneWidth = width;
|
||||
ctx.resolution.paneHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
int VideoOutContext::Open()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
int handle = -1;
|
||||
|
||||
for (int i = 1; i < VIDEO_OUT_NUM_MAX; i++)
|
||||
{
|
||||
if (!m_video_out_ctx[i].opened)
|
||||
{
|
||||
handle = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXIT_IF(m_video_out_ctx[handle].flip_eq != nullptr);
|
||||
EXIT_IF(m_video_out_ctx[handle].flip_rate != 0);
|
||||
|
||||
m_video_out_ctx[handle].opened = true;
|
||||
m_video_out_ctx[handle].flip_status = VideoOutFlipStatus();
|
||||
m_video_out_ctx[handle].flip_status.flipArg = -1;
|
||||
m_video_out_ctx[handle].flip_status.currentBuffer = -1;
|
||||
m_video_out_ctx[handle].flip_status.count = 0;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void VideoOutContext::Close(int handle)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(handle >= VIDEO_OUT_NUM_MAX);
|
||||
EXIT_NOT_IMPLEMENTED(!m_video_out_ctx[handle].opened);
|
||||
|
||||
m_video_out_ctx[handle].opened = false;
|
||||
|
||||
if (m_video_out_ctx[handle].flip_eq != nullptr)
|
||||
{
|
||||
EventQueue::KernelDeleteEvent(m_video_out_ctx[handle].flip_eq, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
|
||||
EXIT_IF(m_video_out_ctx[handle].flip_eq != nullptr);
|
||||
}
|
||||
|
||||
m_video_out_ctx[handle].flip_rate = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
m_video_out_ctx[handle].buffers[i].buffer = nullptr;
|
||||
m_video_out_ctx[handle].buffers[i].buffer_vulkan = nullptr;
|
||||
m_video_out_ctx[handle].buffers[i].buffer_size = 0;
|
||||
m_video_out_ctx[handle].buffers[i].set_id = 0;
|
||||
m_video_out_ctx[handle].buffers_sets[i].num = 0;
|
||||
m_video_out_ctx[handle].buffers_sets[i].start_index = 0;
|
||||
}
|
||||
|
||||
m_video_out_ctx[handle].buffers_sets_num = 0;
|
||||
}
|
||||
|
||||
VideoOutConfig* VideoOutContext::Get(int handle)
|
||||
{
|
||||
EXIT_NOT_IMPLEMENTED(handle >= VIDEO_OUT_NUM_MAX);
|
||||
EXIT_NOT_IMPLEMENTED(!m_video_out_ctx[handle].opened);
|
||||
|
||||
return m_video_out_ctx + handle;
|
||||
}
|
||||
|
||||
VideoOutBufferImageInfo VideoOutContext::FindImage(void* buffer)
|
||||
{
|
||||
VideoOutBufferImageInfo ret;
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& ctx: m_video_out_ctx)
|
||||
{
|
||||
if (ctx.opened)
|
||||
{
|
||||
for (int i = 0; i < ctx.buffers_sets_num; i++)
|
||||
{
|
||||
for (int j = ctx.buffers_sets[i].start_index; j < ctx.buffers_sets[i].num; j++)
|
||||
{
|
||||
if (ctx.buffers[j].buffer == buffer)
|
||||
{
|
||||
ret.image = ctx.buffers[j].buffer_vulkan;
|
||||
ret.buffer_size = ctx.buffers[j].buffer_size;
|
||||
ret.index = j - ctx.buffers_sets[i].start_index;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
END:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FlipQueue::Submit(VideoOutConfig* cfg, int index, int64_t flip_arg)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
if (m_requests.Size() >= 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Request r {};
|
||||
r.cfg = cfg;
|
||||
r.index = index;
|
||||
r.flip_arg = flip_arg;
|
||||
r.submit_tsc = LibKernel::KernelReadTsc();
|
||||
|
||||
m_requests.Add(r);
|
||||
|
||||
cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.Size());
|
||||
cfg->flip_status.gcQueueNum = 0;
|
||||
|
||||
m_submit_cond_var.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipQueue::Wait(VideoOutConfig* cfg, int index)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
while (
|
||||
m_requests.IndexValid(m_requests.Find(cfg, index, [](auto r, auto cfg, auto index) { return r.cfg == cfg && r.index == index; })))
|
||||
{
|
||||
m_done_cond_var.Wait(&m_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
bool FlipQueue::Flip(uint32_t micros)
|
||||
{
|
||||
KYTY_PROFILER_BLOCK("FlipQueue::Flip");
|
||||
|
||||
m_mutex.Lock();
|
||||
if (m_requests.Size() == 0)
|
||||
{
|
||||
m_submit_cond_var.WaitFor(&m_mutex, micros);
|
||||
|
||||
if (m_requests.Size() == 0)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto first = m_requests.First();
|
||||
auto r = m_requests.At(first);
|
||||
m_mutex.Unlock();
|
||||
|
||||
auto* buffer = r.cfg->buffers[r.index].buffer_vulkan;
|
||||
|
||||
// if (buffer->framebuffer == nullptr)
|
||||
// {
|
||||
// // TODO(): Flush via GpuMemoryFlush()
|
||||
// const auto& attribute = r.cfg->buffers_sets[r.cfg->buffers[r.index].set_id].attr;
|
||||
// auto buffer_size = calc_buffer_size(&attribute);
|
||||
// EXIT_NOT_IMPLEMENTED(buffer_size == 0);
|
||||
// Graphics::VideoOutBufferObject vulkan_buffer_info(attribute.pixelFormat, attribute.width, attribute.height,
|
||||
// (attribute.tilingMode == 0), Config::IsNeo());
|
||||
// r.cfg->buffers[r.index].buffer_vulkan = static_cast<Graphics::VideoOutVulkanImage*>(
|
||||
// Graphics::GpuMemoryGetObject(g_video_out_context->GetGraphicCtx(),
|
||||
// reinterpret_cast<uint64_t>(r.cfg->buffers[r.index].buffer), buffer_size, vulkan_buffer_info));
|
||||
// EXIT_NOT_IMPLEMENTED(r.cfg->buffers[r.index].buffer_vulkan != buffer);
|
||||
// }
|
||||
|
||||
Graphics::WindowDrawBuffer(buffer);
|
||||
|
||||
if (r.cfg->flip_eq != nullptr)
|
||||
{
|
||||
auto result = EventQueue::KernelTriggerEvent(r.cfg->flip_eq, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
||||
reinterpret_cast<void*>(r.flip_arg));
|
||||
EXIT_NOT_IMPLEMENTED(result != OK);
|
||||
}
|
||||
|
||||
printf("Flip done: %d\n", r.index);
|
||||
|
||||
m_mutex.Lock();
|
||||
|
||||
m_requests.Remove(first);
|
||||
m_done_cond_var.Signal();
|
||||
|
||||
r.cfg->flip_status.count++;
|
||||
r.cfg->flip_status.processTime = LibKernel::KernelGetProcessTime();
|
||||
r.cfg->flip_status.tsc = LibKernel::KernelReadTsc();
|
||||
r.cfg->flip_status.submitTsc = r.submit_tsc;
|
||||
r.cfg->flip_status.flipArg = r.flip_arg;
|
||||
r.cfg->flip_status.currentBuffer = r.index;
|
||||
r.cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.Size());
|
||||
|
||||
m_mutex.Unlock();
|
||||
|
||||
Graphics::GpuMemoryFrameDone();
|
||||
Graphics::GpuMemoryDbgDump();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipQueue::GetFlipStatus(VideoOutConfig* cfg, VideoOutFlipStatus* out)
|
||||
{
|
||||
EXIT_IF(cfg == nullptr);
|
||||
EXIT_IF(out == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
*out = cfg->flip_status;
|
||||
}
|
||||
|
||||
bool FlipWindow(uint32_t micros)
|
||||
{
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
return g_video_out_context->GetFlipQueue().Flip(micros);
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutOpen(int user_id, int bus_type, int index, const void* param)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(user_id != 255 && user_id != 0);
|
||||
EXIT_NOT_IMPLEMENTED(bus_type != 0);
|
||||
EXIT_NOT_IMPLEMENTED(index != 0);
|
||||
EXIT_NOT_IMPLEMENTED(param != nullptr);
|
||||
|
||||
int handle = g_video_out_context->Open();
|
||||
|
||||
if (handle < 0)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutClose(int handle)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
g_video_out_context->Close(handle);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutGetResolutionStatus(int handle, VideoOutResolutionStatus* status)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(status == nullptr);
|
||||
|
||||
*status = g_video_out_context->Get(handle)->resolution;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI void VideoOutSetBufferAttribute(VideoOutBufferAttribute* attribute, uint32_t pixel_format, uint32_t tiling_mode,
|
||||
uint32_t aspect_ratio, uint32_t width, uint32_t height, uint32_t pitch_in_pixel)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(attribute == nullptr);
|
||||
|
||||
printf("\tpixel_format = %08" PRIx32 "\n", pixel_format);
|
||||
printf("\ttiling_mode = %" PRIu32 "\n", tiling_mode);
|
||||
printf("\taspect_ratio = %" PRIu32 "\n", aspect_ratio);
|
||||
printf("\twidth = %" PRIu32 "\n", width);
|
||||
printf("\theight = %" PRIu32 "\n", height);
|
||||
printf("\tpitch_in_pixel = %" PRIu32 "\n", pitch_in_pixel);
|
||||
|
||||
memset(attribute, 0, sizeof(VideoOutBufferAttribute));
|
||||
|
||||
attribute->pixelFormat = pixel_format;
|
||||
attribute->tilingMode = tiling_mode;
|
||||
attribute->aspectRatio = aspect_ratio;
|
||||
attribute->width = width;
|
||||
attribute->height = height;
|
||||
attribute->pitchInPixel = pitch_in_pixel;
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutSetFlipRate(int handle, int rate)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rate < 0 || rate > 2);
|
||||
|
||||
printf("\trate = %d\n", rate);
|
||||
|
||||
g_video_out_context->Get(handle)->flip_rate = rate;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void flip_event_reset_func(LibKernel::EventQueue::KernelEqueueEvent* event)
|
||||
{
|
||||
EXIT_IF(event == nullptr);
|
||||
event->triggered = false;
|
||||
event->event.fflags = 0;
|
||||
event->event.data = 0;
|
||||
}
|
||||
|
||||
static void flip_event_delete_func(LibKernel::EventQueue::KernelEqueueEvent* event)
|
||||
{
|
||||
EXIT_IF(event == nullptr);
|
||||
EXIT_IF(event->filter.data == nullptr);
|
||||
if (event->filter.data != nullptr)
|
||||
{
|
||||
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
|
||||
EXIT_IF(video_out->flip_eq == nullptr);
|
||||
video_out->flip_eq = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void flip_event_trigger_func(LibKernel::EventQueue::KernelEqueueEvent* event, void* trigger_data)
|
||||
{
|
||||
EXIT_IF(event == nullptr);
|
||||
event->triggered = true;
|
||||
event->event.fflags++;
|
||||
event->event.data = reinterpret_cast<intptr_t>(trigger_data);
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutAddFlipEvent(EventQueue::KernelEqueue eq, int handle, void* udata)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
auto* ctx = g_video_out_context->Get(handle);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(ctx->flip_eq != nullptr);
|
||||
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
|
||||
EventQueue::KernelEqueueEvent event;
|
||||
event.triggered = false;
|
||||
event.event.ident = VIDEO_OUT_EVENT_FLIP;
|
||||
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
|
||||
event.event.udata = udata;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.filter.delete_func = flip_event_delete_func;
|
||||
event.filter.reset_func = flip_event_reset_func;
|
||||
event.filter.trigger_func = flip_event_trigger_func;
|
||||
event.filter.data = ctx;
|
||||
|
||||
int result = EventQueue::KernelAddEvent(eq, event);
|
||||
|
||||
ctx->flip_eq = eq;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutRegisterBuffers(int handle, int start_index, void* const* addresses, int buffer_num,
|
||||
const VideoOutBufferAttribute* attribute)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
auto* ctx = g_video_out_context->Get(handle);
|
||||
|
||||
if (addresses == nullptr)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (attribute == nullptr)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_OPTION;
|
||||
}
|
||||
|
||||
if (start_index < 0 || start_index > 15 || buffer_num < 1 || buffer_num > 16 || start_index + buffer_num > 15)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
Graphics::WindowWaitForGraphicInitialized();
|
||||
Graphics::GraphicsRenderCreateContext();
|
||||
|
||||
int set_index = ctx->buffers_sets_num++;
|
||||
|
||||
if (set_index > 15)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_NO_EMPTY_SLOT;
|
||||
}
|
||||
|
||||
printf("\tstart_index = %d\n", start_index);
|
||||
printf("\tbuffer_num = %d\n", buffer_num);
|
||||
printf("\tpixel_format = 0x%08" PRIx32 "\n", attribute->pixelFormat);
|
||||
printf("\ttiling_mode = %" PRIu32 "\n", attribute->tilingMode);
|
||||
printf("\taspect_ratio = %" PRIu32 "\n", attribute->aspectRatio);
|
||||
printf("\twidth = %" PRIu32 "\n", attribute->width);
|
||||
printf("\theight = %" PRIu32 "\n", attribute->height);
|
||||
printf("\tpitch_in_pixel = %" PRIu32 "\n", attribute->pitchInPixel);
|
||||
printf("\toption = %" PRIu32 "\n", attribute->option);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(attribute->pixelFormat != 0x80000000);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->tilingMode != 0);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->aspectRatio != 0);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->pitchInPixel != attribute->width);
|
||||
EXIT_NOT_IMPLEMENTED(attribute->option != 0);
|
||||
|
||||
auto buffer_size = calc_buffer_size(attribute);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer_size == 0);
|
||||
|
||||
ctx->buffers_sets[set_index].start_index = start_index;
|
||||
ctx->buffers_sets[set_index].num = buffer_num;
|
||||
ctx->buffers_sets[set_index].attr = *attribute;
|
||||
|
||||
Graphics::VideoOutBufferObject vulkan_buffer_info(attribute->pixelFormat, attribute->width, attribute->height,
|
||||
(attribute->tilingMode == 0), Config::IsNeo());
|
||||
|
||||
for (int i = 0; i < buffer_num; i++)
|
||||
{
|
||||
if (ctx->buffers[i + start_index].buffer != nullptr)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_SLOT_OCCUPIED;
|
||||
}
|
||||
|
||||
ctx->buffers[i + start_index].set_id = set_index;
|
||||
ctx->buffers[i + start_index].buffer = addresses[i];
|
||||
ctx->buffers[i + start_index].buffer_size = buffer_size;
|
||||
ctx->buffers[i + start_index].buffer_vulkan = static_cast<Graphics::VideoOutVulkanImage*>(Graphics::GpuMemoryGetObject(
|
||||
g_video_out_context->GetGraphicCtx(), reinterpret_cast<uint64_t>(addresses[i]), buffer_size, vulkan_buffer_info));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(ctx->buffers[i + start_index].buffer_vulkan == nullptr);
|
||||
|
||||
printf("\tbuffers[%d] = %016" PRIx64 "\n", i + start_index, reinterpret_cast<uint64_t>(addresses[i]));
|
||||
}
|
||||
|
||||
// Graphics::GpuMemoryDbgDump();
|
||||
|
||||
return set_index;
|
||||
}
|
||||
|
||||
VideoOutBufferImageInfo VideoOutGetImage(uint64_t addr)
|
||||
{
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
return g_video_out_context->FindImage(reinterpret_cast<void*>(addr));
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
auto* ctx = g_video_out_context->Get(handle);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(flip_mode != 1);
|
||||
|
||||
if (index < 0 || index > 15)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_INDEX;
|
||||
}
|
||||
|
||||
if (!g_video_out_context->GetFlipQueue().Submit(ctx, index, flip_arg))
|
||||
{
|
||||
return VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void VideoOutWaitFlipDone(int handle, int index)
|
||||
{
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
auto* ctx = g_video_out_context->Get(handle);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(index < 0 || index > 15);
|
||||
|
||||
g_video_out_context->GetFlipQueue().Wait(ctx, index);
|
||||
}
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutGetFlipStatus(int handle, VideoOutFlipStatus* status)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_video_out_context == nullptr);
|
||||
|
||||
if (status == nullptr)
|
||||
{
|
||||
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* ctx = g_video_out_context->Get(handle);
|
||||
|
||||
g_video_out_context->GetFlipQueue().GetFlipStatus(ctx, status);
|
||||
|
||||
printf("\t count = %" PRIu64 "\n", status->count);
|
||||
printf("\t processTime = %" PRIu64 "\n", status->processTime);
|
||||
printf("\t tsc = %" PRIu64 "\n", status->tsc);
|
||||
printf("\t submitTsc = %" PRIu64 "\n", status->submitTsc);
|
||||
printf("\t flipArg = %" PRId64 "\n", status->flipArg);
|
||||
printf("\t gcQueueNum = %d\n", status->gcQueueNum);
|
||||
printf("\t flipPendingNum = %d\n", status->flipPendingNum);
|
||||
printf("\t currentBuffer = %d\n", status->currentBuffer);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::VideoOut
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "Emulator/Graphics/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_R8G8B8A8_SRGB;
|
||||
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 | 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);
|
||||
|
||||
// vkBindImageMemory(ctx->device, vk_obj->image, mem->memory, mem->offset);
|
||||
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_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);
|
||||
|
||||
if (tiled && buffer_is_tiled(*vaddr, *size))
|
||||
{
|
||||
auto* temp_buf = new uint8_t[*size];
|
||||
TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::VideoOutTiled,
|
||||
params[VideoOutBufferObject::PARAM_WIDTH], params[VideoOutBufferObject::PARAM_HEIGHT], neo);
|
||||
UtilFillImage(ctx, vk_obj, temp_buf, *size);
|
||||
delete[] temp_buf;
|
||||
} else
|
||||
{
|
||||
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user