2021-06-09 19:54:31 +02:00
|
|
|
/**
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Copyright 2021 Ben Vanik. All rights reserved. *
|
|
|
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "xenia/gpu/vulkan/vulkan_primitive_processor.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "xenia/base/assert.h"
|
|
|
|
|
#include "xenia/base/logging.h"
|
|
|
|
|
#include "xenia/gpu/vulkan/deferred_command_buffer.h"
|
|
|
|
|
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
|
|
|
|
#include "xenia/ui/vulkan/vulkan_util.h"
|
|
|
|
|
|
|
|
|
|
namespace xe {
|
|
|
|
|
namespace gpu {
|
|
|
|
|
namespace vulkan {
|
|
|
|
|
|
|
|
|
|
VulkanPrimitiveProcessor::~VulkanPrimitiveProcessor() { Shutdown(true); }
|
|
|
|
|
|
|
|
|
|
bool VulkanPrimitiveProcessor::Initialize() {
|
2025-08-12 22:21:59 +02:00
|
|
|
const ui::vulkan::VulkanDevice* const vulkan_device =
|
|
|
|
|
command_processor_.GetVulkanDevice();
|
|
|
|
|
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
|
|
|
|
vulkan_device->properties();
|
|
|
|
|
if (!InitializeCommon(
|
|
|
|
|
device_properties.fullDrawIndexUint32, device_properties.triangleFans,
|
|
|
|
|
false, device_properties.geometryShader,
|
|
|
|
|
device_properties.geometryShader, device_properties.geometryShader)) {
|
2021-06-09 19:54:31 +02:00
|
|
|
Shutdown();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
frame_index_buffer_pool_ =
|
|
|
|
|
std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
|
2025-08-12 22:21:59 +02:00
|
|
|
vulkan_device, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
2021-06-09 19:54:31 +02:00
|
|
|
std::max(size_t(kMinRequiredConvertedIndexBufferSize),
|
|
|
|
|
ui::GraphicsUploadBufferPool::kDefaultPageSize));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::Shutdown(bool from_destructor) {
|
2025-08-12 22:21:59 +02:00
|
|
|
const ui::vulkan::VulkanDevice* const vulkan_device =
|
|
|
|
|
command_processor_.GetVulkanDevice();
|
|
|
|
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
|
|
|
|
const VkDevice device = vulkan_device->device();
|
2021-06-09 19:54:31 +02:00
|
|
|
|
|
|
|
|
frame_index_buffers_.clear();
|
|
|
|
|
frame_index_buffer_pool_.reset();
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_upload_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_upload_memory_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_memory_);
|
|
|
|
|
|
|
|
|
|
if (!from_destructor) {
|
|
|
|
|
ShutdownCommon();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::CompletedSubmissionUpdated() {
|
|
|
|
|
if (builtin_index_buffer_upload_ != VK_NULL_HANDLE &&
|
|
|
|
|
command_processor_.GetCompletedSubmission() >=
|
|
|
|
|
builtin_index_buffer_upload_submission_) {
|
2025-08-12 22:21:59 +02:00
|
|
|
const ui::vulkan::VulkanDevice* const vulkan_device =
|
|
|
|
|
command_processor_.GetVulkanDevice();
|
|
|
|
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
|
|
|
|
const VkDevice device = vulkan_device->device();
|
2021-06-09 19:54:31 +02:00
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_upload_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_upload_memory_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::BeginSubmission() {
|
|
|
|
|
if (builtin_index_buffer_upload_ != VK_NULL_HANDLE &&
|
|
|
|
|
builtin_index_buffer_upload_submission_ == UINT64_MAX) {
|
|
|
|
|
// No need to submit deferred barriers - builtin_index_buffer_ has never
|
|
|
|
|
// been used yet, and builtin_index_buffer_upload_ is written before
|
|
|
|
|
// submitting commands reading it.
|
|
|
|
|
|
2022-03-21 21:02:51 +01:00
|
|
|
command_processor_.EndRenderPass();
|
|
|
|
|
|
2021-06-09 19:54:31 +02:00
|
|
|
DeferredCommandBuffer& command_buffer =
|
|
|
|
|
command_processor_.deferred_command_buffer();
|
|
|
|
|
|
|
|
|
|
VkBufferCopy* copy_region = command_buffer.CmdCopyBufferEmplace(
|
|
|
|
|
builtin_index_buffer_upload_, builtin_index_buffer_, 1);
|
|
|
|
|
copy_region->srcOffset = 0;
|
|
|
|
|
copy_region->dstOffset = 0;
|
|
|
|
|
copy_region->size = builtin_index_buffer_size_;
|
|
|
|
|
|
2022-03-21 21:02:51 +01:00
|
|
|
command_processor_.PushBufferMemoryBarrier(
|
|
|
|
|
builtin_index_buffer_, 0, VK_WHOLE_SIZE, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
|
|
|
VK_ACCESS_INDEX_READ_BIT);
|
2021-06-09 19:54:31 +02:00
|
|
|
|
|
|
|
|
builtin_index_buffer_upload_submission_ =
|
|
|
|
|
command_processor_.GetCurrentSubmission();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::BeginFrame() {
|
|
|
|
|
frame_index_buffer_pool_->Reclaim(command_processor_.GetCompletedFrame());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::EndSubmission() {
|
|
|
|
|
frame_index_buffer_pool_->FlushWrites();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanPrimitiveProcessor::EndFrame() {
|
|
|
|
|
ClearPerFrameCache();
|
|
|
|
|
frame_index_buffers_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-27 16:14:28 +02:00
|
|
|
bool VulkanPrimitiveProcessor::InitializeBuiltinIndexBuffer(
|
|
|
|
|
size_t size_bytes, std::function<void(void*)> fill_callback) {
|
|
|
|
|
assert_not_zero(size_bytes);
|
2021-06-09 19:54:31 +02:00
|
|
|
assert_true(builtin_index_buffer_ == VK_NULL_HANDLE);
|
|
|
|
|
assert_true(builtin_index_buffer_memory_ == VK_NULL_HANDLE);
|
|
|
|
|
assert_true(builtin_index_buffer_upload_ == VK_NULL_HANDLE);
|
|
|
|
|
assert_true(builtin_index_buffer_upload_memory_ == VK_NULL_HANDLE);
|
|
|
|
|
|
2025-08-12 22:21:59 +02:00
|
|
|
const ui::vulkan::VulkanDevice* const vulkan_device =
|
|
|
|
|
command_processor_.GetVulkanDevice();
|
|
|
|
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
|
|
|
|
const VkDevice device = vulkan_device->device();
|
2021-06-09 19:54:31 +02:00
|
|
|
|
2022-07-27 16:14:28 +02:00
|
|
|
builtin_index_buffer_size_ = VkDeviceSize(size_bytes);
|
2021-06-09 19:54:31 +02:00
|
|
|
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
2025-08-12 22:21:59 +02:00
|
|
|
vulkan_device, builtin_index_buffer_size_,
|
2021-06-09 19:54:31 +02:00
|
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
|
|
|
ui::vulkan::util::MemoryPurpose::kDeviceLocal, builtin_index_buffer_,
|
|
|
|
|
builtin_index_buffer_memory_)) {
|
|
|
|
|
XELOGE(
|
|
|
|
|
"Vulkan primitive processor: Failed to create the built-in index "
|
2022-07-27 16:14:28 +02:00
|
|
|
"buffer GPU resource with {} bytes",
|
|
|
|
|
size_bytes);
|
2021-06-09 19:54:31 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
uint32_t upload_memory_type;
|
|
|
|
|
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
2025-08-12 22:21:59 +02:00
|
|
|
vulkan_device, builtin_index_buffer_size_,
|
2021-06-09 19:54:31 +02:00
|
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
|
|
|
ui::vulkan::util::MemoryPurpose::kUpload,
|
|
|
|
|
builtin_index_buffer_upload_, builtin_index_buffer_upload_memory_,
|
|
|
|
|
&upload_memory_type)) {
|
|
|
|
|
XELOGE(
|
|
|
|
|
"Vulkan primitive processor: Failed to create the built-in index "
|
2022-07-27 16:14:28 +02:00
|
|
|
"buffer upload resource with {} bytes",
|
|
|
|
|
size_bytes);
|
2021-06-09 19:54:31 +02:00
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_memory_);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* mapping;
|
|
|
|
|
if (dfn.vkMapMemory(device, builtin_index_buffer_upload_memory_, 0,
|
|
|
|
|
VK_WHOLE_SIZE, 0, &mapping) != VK_SUCCESS) {
|
|
|
|
|
XELOGE(
|
|
|
|
|
"Vulkan primitive processor: Failed to map the built-in index buffer "
|
2022-07-27 16:14:28 +02:00
|
|
|
"upload resource with {} bytes",
|
|
|
|
|
size_bytes);
|
2021-06-09 19:54:31 +02:00
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_upload_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_upload_memory_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
|
|
|
|
builtin_index_buffer_);
|
|
|
|
|
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
|
|
|
|
builtin_index_buffer_memory_);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-07-27 16:14:28 +02:00
|
|
|
fill_callback(mapping);
|
2021-06-09 19:54:31 +02:00
|
|
|
ui::vulkan::util::FlushMappedMemoryRange(
|
2025-08-12 22:21:59 +02:00
|
|
|
vulkan_device, builtin_index_buffer_memory_, upload_memory_type);
|
2022-02-03 19:12:10 +01:00
|
|
|
dfn.vkUnmapMemory(device, builtin_index_buffer_upload_memory_);
|
2021-06-09 19:54:31 +02:00
|
|
|
|
|
|
|
|
// Schedule uploading in the first submission.
|
|
|
|
|
builtin_index_buffer_upload_submission_ = UINT64_MAX;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* VulkanPrimitiveProcessor::RequestHostConvertedIndexBufferForCurrentFrame(
|
|
|
|
|
xenos::IndexFormat format, uint32_t index_count, bool coalign_for_simd,
|
|
|
|
|
uint32_t coalignment_original_address, size_t& backend_handle_out) {
|
|
|
|
|
size_t index_size = format == xenos::IndexFormat::kInt16 ? sizeof(uint16_t)
|
|
|
|
|
: sizeof(uint32_t);
|
|
|
|
|
VkBuffer buffer;
|
|
|
|
|
VkDeviceSize offset;
|
|
|
|
|
uint8_t* mapping = frame_index_buffer_pool_->Request(
|
|
|
|
|
command_processor_.GetCurrentFrame(),
|
|
|
|
|
index_size * index_count +
|
|
|
|
|
(coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0),
|
|
|
|
|
index_size, buffer, offset);
|
|
|
|
|
if (!mapping) {
|
2022-02-03 19:12:10 +01:00
|
|
|
return nullptr;
|
2021-06-09 19:54:31 +02:00
|
|
|
}
|
|
|
|
|
if (coalign_for_simd) {
|
|
|
|
|
ptrdiff_t coalignment_offset =
|
|
|
|
|
GetSimdCoalignmentOffset(mapping, coalignment_original_address);
|
|
|
|
|
mapping += coalignment_offset;
|
|
|
|
|
offset = VkDeviceSize(offset + coalignment_offset);
|
|
|
|
|
}
|
|
|
|
|
backend_handle_out = frame_index_buffers_.size();
|
|
|
|
|
frame_index_buffers_.emplace_back(buffer, offset);
|
|
|
|
|
return mapping;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace vulkan
|
|
|
|
|
} // namespace gpu
|
|
|
|
|
} // namespace xe
|