Initial commit

This commit is contained in:
InoriRus
2021-12-01 19:29:27 +10:00
parent b1e7dcdc5d
commit 43f49c8763
1843 changed files with 1111694 additions and 0 deletions
@@ -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
+923
View File
@@ -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
+544
View File
@@ -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
+349
View File
@@ -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
+150
View File
@@ -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
+244
View File
@@ -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
+674
View File
@@ -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
+372
View File
@@ -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, &region);
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, &region, 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, &copy_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
+722
View File
@@ -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