mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 16:51:20 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6f6e306e0 | ||
|
|
b847135321 | ||
|
|
ca3dc8792a | ||
|
|
ac3156df3a | ||
|
|
6aa6e390ee | ||
|
|
e4c10de8b6 | ||
|
|
b3b3aeef73 | ||
|
|
056dcee70c | ||
|
|
13c5bbe916 | ||
|
|
bbe3bd0304 |
No files matched your search
@@ -14,7 +14,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct VulkanBuffer;
|
||||
struct VulkanImage;
|
||||
struct VulkanMemory;
|
||||
|
||||
@@ -33,6 +32,7 @@ struct GraphicContext {
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
uint32_t subgroup_size = 0;
|
||||
@@ -118,11 +118,9 @@ private:
|
||||
};
|
||||
|
||||
struct VulkanMemory {
|
||||
vk::MemoryRequirements requirements = {};
|
||||
vk::MemoryPropertyFlags property = {};
|
||||
vk::MemoryPropertyFlags preferred_property = {};
|
||||
VmaAllocation allocation = nullptr;
|
||||
uint32_t type = 0;
|
||||
};
|
||||
|
||||
struct VulkanImageState {
|
||||
@@ -138,7 +136,6 @@ struct VulkanImage {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::ImageType image_type = vk::ImageType::e2D;
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
uint32_t guest_pitch = 0;
|
||||
uint32_t layers = 1;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t samples = 1;
|
||||
@@ -150,12 +147,7 @@ struct VulkanImage {
|
||||
Graphics::VulkanMemory memory;
|
||||
};
|
||||
|
||||
struct VulkanBuffer {
|
||||
vk::Buffer buffer = nullptr;
|
||||
VulkanMemory memory;
|
||||
vk::BufferUsageFlags usage = {};
|
||||
uint64_t buffer_size = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
+11
-30
@@ -4,7 +4,6 @@
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
@@ -64,7 +63,7 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsage usage,
|
||||
uint64_t cpu_address, vk::BufferUsageFlags flags, uint64_t size)
|
||||
: m_graphics(&graphics), m_scheduler(&scheduler), m_usage(usage), m_cpu_address(cpu_address),
|
||||
m_buffer(std::make_unique<VulkanBuffer>()) {
|
||||
m_size(size) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(graphics.allocator == nullptr || size == 0);
|
||||
|
||||
@@ -87,53 +86,35 @@ Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsag
|
||||
VkBuffer native_buffer = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(vmaCreateBuffer(
|
||||
graphics.allocator, static_cast<const VkBufferCreateInfo*>(buffer_info), &allocation_info,
|
||||
&native_buffer, &m_buffer->memory.allocation, &allocation_result));
|
||||
&native_buffer, &m_allocation, &allocation_result));
|
||||
if (result != vk::Result::eSuccess) {
|
||||
graphics.LogMemoryBudget();
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
|
||||
m_buffer->buffer = native_buffer;
|
||||
m_buffer->usage = flags;
|
||||
m_buffer->buffer_size = size;
|
||||
m_buffer->memory.type = allocation_result.memoryType;
|
||||
graphics.device.getBufferMemoryRequirements(m_buffer->buffer, &m_buffer->memory.requirements);
|
||||
if (static_cast<bool>(flags & vk::BufferUsageFlagBits::eShaderDeviceAddress)) {
|
||||
m_buffer = native_buffer;
|
||||
if (with_bda) {
|
||||
vk::BufferDeviceAddressInfo address_info {};
|
||||
address_info.buffer = m_buffer->buffer;
|
||||
address_info.buffer = m_buffer;
|
||||
m_device_address = graphics.device.getBufferAddress(address_info);
|
||||
EXIT_IF(m_device_address == 0);
|
||||
}
|
||||
|
||||
VkMemoryPropertyFlags properties = 0;
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_buffer->memory.allocation, &properties);
|
||||
m_buffer->memory.property = vk::MemoryPropertyFlags(properties);
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_allocation, &properties);
|
||||
m_coherent = (properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||
if (allocation_result.pMappedData != nullptr) {
|
||||
m_mapped = {static_cast<uint8_t*>(allocation_result.pMappedData),
|
||||
static_cast<size_t>(size)};
|
||||
}
|
||||
VulkanTrackAllocation(m_buffer->memory);
|
||||
}
|
||||
|
||||
Buffer::~Buffer() {
|
||||
if (m_buffer->buffer != nullptr) {
|
||||
VulkanUntrackAllocation(m_buffer->memory);
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer->buffer, m_buffer->memory.allocation);
|
||||
if (m_buffer != nullptr) {
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer, m_allocation);
|
||||
}
|
||||
}
|
||||
|
||||
vk::Buffer Buffer::Handle() const noexcept {
|
||||
return m_buffer->buffer;
|
||||
}
|
||||
|
||||
uint64_t Buffer::Size() const noexcept {
|
||||
return m_buffer->buffer_size;
|
||||
}
|
||||
|
||||
bool Buffer::IsCoherent() const noexcept {
|
||||
return bool(m_buffer->memory.property & vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
}
|
||||
|
||||
vk::DeviceAddress Buffer::BufferDeviceAddress() const noexcept {
|
||||
EXIT_IF(m_device_address == 0);
|
||||
return m_device_address;
|
||||
@@ -153,7 +134,7 @@ void Buffer::Flush(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_mapped.empty() || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaFlushAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||
vmaFlushAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
@@ -162,7 +143,7 @@ void Buffer::Invalidate(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_usage != MemoryUsage::Download || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaInvalidateAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||
vmaInvalidateAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-6
@@ -6,19 +6,19 @@
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocation)
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
struct StreamBufferTestAccess;
|
||||
struct GraphicContext;
|
||||
struct VulkanBuffer;
|
||||
|
||||
enum class MemoryUsage : uint8_t {
|
||||
DeviceLocal,
|
||||
@@ -42,10 +42,10 @@ public:
|
||||
~Buffer();
|
||||
KYTY_CLASS_NO_COPY(Buffer);
|
||||
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept;
|
||||
[[nodiscard]] uint64_t Size() const noexcept;
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept { return m_buffer; }
|
||||
[[nodiscard]] uint64_t Size() const noexcept { return m_size; }
|
||||
[[nodiscard]] std::span<uint8_t> Mapped() const noexcept { return m_mapped; }
|
||||
[[nodiscard]] bool IsCoherent() const noexcept;
|
||||
[[nodiscard]] bool IsCoherent() const noexcept { return m_coherent; }
|
||||
[[nodiscard]] MemoryUsage Usage() const noexcept { return m_usage; }
|
||||
[[nodiscard]] uint64_t CpuAddress() const noexcept { return m_cpu_address; }
|
||||
[[nodiscard]] vk::DeviceAddress BufferDeviceAddress() const noexcept;
|
||||
@@ -88,7 +88,10 @@ private:
|
||||
MemoryUsage m_usage = MemoryUsage::DeviceLocal;
|
||||
uint64_t m_cpu_address = 0;
|
||||
vk::DeviceAddress m_device_address = 0;
|
||||
std::unique_ptr<VulkanBuffer> m_buffer;
|
||||
vk::Buffer m_buffer = nullptr;
|
||||
VmaAllocation m_allocation = nullptr;
|
||||
uint64_t m_size;
|
||||
bool m_coherent = false;
|
||||
std::span<uint8_t> m_mapped;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -26,12 +26,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const ImageInfo& info) {
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const GraphicContext& graphics,
|
||||
const ImageInfo& info) {
|
||||
vk::ImageCreateFlags flags {};
|
||||
if (DepthAspectTransferFormat(info.pixel_format) == vk::Format::eUndefined) {
|
||||
flags |= vk::ImageCreateFlagBits::eMutableFormat;
|
||||
flags |= vk::ImageCreateFlagBits::eExtendedUsage;
|
||||
if (Prospero::BlockCompressedBytesPerBlock(info.guest_format) != 0) {
|
||||
if (info.IsBlock() && graphics.supports_block_texel_view) {
|
||||
flags |= vk::ImageCreateFlagBits::eBlockTexelViewCompatible;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +48,10 @@ namespace {
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageUsageFlags ImageUsageFlags(GraphicContext& graphics, const ImageInfo& info) {
|
||||
if (info.IsBlock()) {
|
||||
return vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eSampled;
|
||||
}
|
||||
const auto properties = graphics.GetFormatProperties(info.pixel_format);
|
||||
auto usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eSampledImage)) {
|
||||
@@ -659,11 +664,10 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
backing.format = info.pixel_format;
|
||||
backing.image_type = HostImageType(info.type);
|
||||
backing.extent = info.extent;
|
||||
backing.guest_pitch = info.pitch;
|
||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
backing.mip_levels = info.resources.levels;
|
||||
backing.samples = info.samples;
|
||||
backing.flags = ImageCreateFlags(info);
|
||||
backing.flags = ImageCreateFlags(graphics, info);
|
||||
backing.usage = ImageUsageFlags(graphics, info);
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
|
||||
|
||||
@@ -655,8 +655,11 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
const auto vs_id = vertex_program.id;
|
||||
const auto ps_id = ps_active ? pixel_program.id : 0;
|
||||
|
||||
PipelineStaticParameters static_params {};
|
||||
PipelineRenderingState rendering {};
|
||||
GraphicsPipelineKey key {};
|
||||
key.vs_shader_id = vs_id;
|
||||
key.ps_shader_id = ps_id;
|
||||
auto& static_params = key.static_params;
|
||||
auto& rendering = key.rendering;
|
||||
rendering.color_count = color_count;
|
||||
uint32_t attachment_samples = 0;
|
||||
for (uint32_t i = 0; i < color_count; i++) {
|
||||
@@ -741,11 +744,6 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
static_params.blend_enable[i] = bc.enable;
|
||||
static_params.blend_bypass[i] = rt.info.blend_bypass;
|
||||
}
|
||||
GraphicsPipelineKey key {};
|
||||
key.rendering = rendering;
|
||||
key.vs_shader_id = vs_id;
|
||||
key.ps_shader_id = ps_id;
|
||||
key.static_params = static_params;
|
||||
if (vs_input_info.stage.program->stage != ShaderType::Mesh) {
|
||||
EXIT_IF(vs_input_info.buffers_num < 0 ||
|
||||
vs_input_info.buffers_num > ShaderVertexInputInfo::RES_MAX ||
|
||||
|
||||
@@ -850,7 +850,14 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
case Prospero::PrimitiveType::kQuadListLegacy:
|
||||
topology = vk::PrimitiveTopology::eTriangleFan;
|
||||
break;
|
||||
default: EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
default: {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
std::printf("Skipping draw with unknown primitive type: %u\n",
|
||||
static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include "kernel/pthread.h"
|
||||
#include "libs/errno.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace Libs::Graphics::Sync {
|
||||
|
||||
@@ -55,70 +53,9 @@ uint64_t ReadReferenceClock() {
|
||||
return value;
|
||||
}
|
||||
|
||||
enum class EndOfPipeCompletion { None, Interrupt, Flip, FlipAndInterrupt };
|
||||
|
||||
struct EndOfPipeSignal {
|
||||
CommandBuffer* buffer = nullptr;
|
||||
uint64_t submit_id = 0;
|
||||
CommandBufferDebugOp debug_operation = CommandBufferDebugOp::Unknown;
|
||||
std::array<uint32_t, 4> debug_args = {};
|
||||
uint64_t debug_data = 0;
|
||||
std::optional<uint64_t> destination;
|
||||
EndOfPipeCompletion completion = EndOfPipeCompletion::None;
|
||||
uint64_t completion_data = 0;
|
||||
int interrupt_event_id = 0;
|
||||
};
|
||||
|
||||
enum class EndOfPipeWriteSize : uint32_t { Dword = 4, Qword = 8 };
|
||||
enum class EndOfPipeWriteAction { Write, WriteBack, Interrupt, InterruptWriteBack };
|
||||
|
||||
static void ValidateEndOfPipeSignal(const EndOfPipeSignal& signal) {
|
||||
if (signal.destination.has_value()) {
|
||||
EXIT_IF(*signal.destination == 0);
|
||||
}
|
||||
EXIT_IF(signal.buffer == nullptr);
|
||||
(void)signal.buffer->Handle();
|
||||
}
|
||||
|
||||
static void RecordEndOfPipeSignal(const EndOfPipeSignal& signal) {
|
||||
ValidateEndOfPipeSignal(signal);
|
||||
signal.buffer->SetDebugInfo(static_cast<uint32_t>(signal.debug_operation), signal.submit_id,
|
||||
signal.debug_args[0], signal.debug_args[1], signal.debug_args[2],
|
||||
signal.debug_args[3], signal.debug_data);
|
||||
|
||||
auto& renderer = signal.buffer->GetContext();
|
||||
auto& scheduler = renderer.GetCommandScheduler();
|
||||
if (signal.completion != EndOfPipeCompletion::None) {
|
||||
EXIT_IF(!scheduler.Active() || signal.buffer != &scheduler.Current());
|
||||
}
|
||||
switch (signal.completion) {
|
||||
case EndOfPipeCompletion::None: return;
|
||||
case EndOfPipeCompletion::Interrupt: {
|
||||
const auto context_id = static_cast<uint32_t>(signal.completion_data);
|
||||
const auto event_id = signal.interrupt_event_id;
|
||||
scheduler.DeferPriorityOperation([&renderer, event_id, context_id] {
|
||||
renderer.TriggerInterrupt(event_id, context_id);
|
||||
});
|
||||
return;
|
||||
}
|
||||
case EndOfPipeCompletion::Flip: {
|
||||
const auto request_id = signal.completion_data;
|
||||
scheduler.DeferPriorityOperation(
|
||||
[&renderer, request_id] { renderer.GetVideoOut().CompleteFlip(request_id); });
|
||||
return;
|
||||
}
|
||||
case EndOfPipeCompletion::FlipAndInterrupt: {
|
||||
const auto request_id = signal.completion_data;
|
||||
const auto event_id = signal.interrupt_event_id;
|
||||
scheduler.DeferPriorityOperation([&renderer, event_id, request_id] {
|
||||
renderer.GetVideoOut().CompleteFlip(request_id);
|
||||
renderer.TriggerInterrupt(event_id, 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CommandBufferDebugOp DebugOperation(EndOfPipeWriteAction action) {
|
||||
switch (action) {
|
||||
case EndOfPipeWriteAction::Write: return CommandBufferDebugOp::EopWrite;
|
||||
@@ -139,24 +76,20 @@ static void RecordEndOfPipeWrite(uint64_t submit_id, CommandBuffer& buffer, uint
|
||||
uint64_t value, EndOfPipeWriteSize size,
|
||||
EndOfPipeWriteAction action, int interrupt_event_id = 0,
|
||||
uint32_t context_id = 0) {
|
||||
EXIT_IF(destination == 0);
|
||||
(void)buffer.Handle();
|
||||
|
||||
const auto width = static_cast<uint32_t>(size);
|
||||
const auto value_low = static_cast<uint32_t>(value);
|
||||
const auto value_high = static_cast<uint32_t>(value >> 32u);
|
||||
const bool interrupt = TriggersInterrupt(action);
|
||||
|
||||
EndOfPipeSignal signal {
|
||||
.buffer = &buffer,
|
||||
.submit_id = submit_id,
|
||||
.debug_operation = DebugOperation(action),
|
||||
.debug_args = interrupt ? std::array {width, context_id, value_low, value_high}
|
||||
: std::array {width, value_low, value_high, 0u},
|
||||
.debug_data = destination,
|
||||
.destination = destination,
|
||||
.completion = interrupt ? EndOfPipeCompletion::Interrupt : EndOfPipeCompletion::None,
|
||||
.completion_data = interrupt ? context_id : value,
|
||||
.interrupt_event_id = interrupt_event_id,
|
||||
};
|
||||
RecordEndOfPipeSignal(signal);
|
||||
const auto operation = static_cast<uint32_t>(DebugOperation(action));
|
||||
if (TriggersInterrupt(action)) {
|
||||
buffer.SetDebugInfo(operation, submit_id, width, context_id, value_low, value_high,
|
||||
destination);
|
||||
TriggerEopEventAtEndOfPipe(buffer, interrupt_event_id, context_id);
|
||||
} else {
|
||||
buffer.SetDebugInfo(operation, submit_id, width, value_low, value_high, 0, destination);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAtEndOfPipe32(uint64_t submit_id, CommandBuffer& buffer, uint32_t* dst_gpu_addr,
|
||||
@@ -167,15 +100,10 @@ void WriteAtEndOfPipe32(uint64_t submit_id, CommandBuffer& buffer, uint32_t* dst
|
||||
|
||||
void WriteAtEndOfPipeGds32(uint64_t submit_id, CommandBuffer& buffer, uint32_t* dst_gpu_addr,
|
||||
uint32_t dw_offset, uint32_t dw_num) {
|
||||
const auto destination = reinterpret_cast<uint64_t>(dst_gpu_addr);
|
||||
RecordEndOfPipeSignal({
|
||||
.buffer = &buffer,
|
||||
.submit_id = submit_id,
|
||||
.debug_operation = CommandBufferDebugOp::EopWrite,
|
||||
.debug_args = {dw_offset, dw_num, 0, 0},
|
||||
.debug_data = destination,
|
||||
.destination = destination,
|
||||
});
|
||||
EXIT_IF(dst_gpu_addr == nullptr);
|
||||
(void)buffer.Handle();
|
||||
buffer.SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopWrite), submit_id,
|
||||
dw_offset, dw_num, 0, 0, reinterpret_cast<uint64_t>(dst_gpu_addr));
|
||||
}
|
||||
|
||||
void WriteAtEndOfPipe64(uint64_t submit_id, CommandBuffer& buffer, uint64_t* dst_gpu_addr,
|
||||
@@ -273,54 +201,53 @@ void WriteAtEndOfPipeWithInterruptWriteBackFlip32(uint64_t submit_id, CommandBuf
|
||||
int handle, int index, int flip_mode,
|
||||
int64_t flip_arg, uint64_t request_id,
|
||||
int event_id) {
|
||||
const auto destination = reinterpret_cast<uint64_t>(dst_gpu_addr);
|
||||
RecordEndOfPipeSignal({
|
||||
.buffer = &buffer,
|
||||
.submit_id = submit_id,
|
||||
.debug_operation = CommandBufferDebugOp::EopWriteBackFlip,
|
||||
.debug_args = {static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), value},
|
||||
.debug_data = static_cast<uint64_t>(flip_arg),
|
||||
.destination = destination,
|
||||
.completion = EndOfPipeCompletion::FlipAndInterrupt,
|
||||
.completion_data = request_id,
|
||||
.interrupt_event_id = event_id,
|
||||
EXIT_IF(dst_gpu_addr == nullptr);
|
||||
(void)buffer.Handle();
|
||||
buffer.SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopWriteBackFlip), submit_id,
|
||||
static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), value, static_cast<uint64_t>(flip_arg));
|
||||
|
||||
auto& renderer = buffer.GetContext();
|
||||
auto& scheduler = renderer.GetCommandScheduler();
|
||||
EXIT_IF(!scheduler.Active() || &buffer != &scheduler.Current());
|
||||
scheduler.DeferPriorityOperation([&renderer, event_id, request_id] {
|
||||
renderer.GetVideoOut().CompleteFlip(request_id);
|
||||
renderer.TriggerInterrupt(event_id, 0);
|
||||
});
|
||||
}
|
||||
|
||||
void WriteAtEndOfPipeWithFlip32(uint64_t submit_id, CommandBuffer& buffer, uint32_t* dst_gpu_addr,
|
||||
uint32_t value, int handle, int index, int flip_mode,
|
||||
int64_t flip_arg, uint64_t request_id) {
|
||||
const auto destination = reinterpret_cast<uint64_t>(dst_gpu_addr);
|
||||
RecordEndOfPipeSignal({
|
||||
.buffer = &buffer,
|
||||
.submit_id = submit_id,
|
||||
.debug_operation = CommandBufferDebugOp::EopFlip,
|
||||
.debug_args = {static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), value},
|
||||
.debug_data = static_cast<uint64_t>(flip_arg),
|
||||
.destination = destination,
|
||||
.completion = EndOfPipeCompletion::Flip,
|
||||
.completion_data = request_id,
|
||||
});
|
||||
EXIT_IF(dst_gpu_addr == nullptr);
|
||||
(void)buffer.Handle();
|
||||
buffer.SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopFlip), submit_id,
|
||||
static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), value, static_cast<uint64_t>(flip_arg));
|
||||
|
||||
auto& renderer = buffer.GetContext();
|
||||
auto& scheduler = renderer.GetCommandScheduler();
|
||||
EXIT_IF(!scheduler.Active() || &buffer != &scheduler.Current());
|
||||
scheduler.DeferPriorityOperation(
|
||||
[&renderer, request_id] { renderer.GetVideoOut().CompleteFlip(request_id); });
|
||||
}
|
||||
|
||||
void WriteAtEndOfPipeOnlyFlip(uint64_t submit_id, CommandBuffer& buffer, int handle, int index,
|
||||
int flip_mode, int64_t flip_arg, uint64_t request_id) {
|
||||
RecordEndOfPipeSignal({
|
||||
.buffer = &buffer,
|
||||
.submit_id = submit_id,
|
||||
.debug_operation = CommandBufferDebugOp::EopOnlyFlip,
|
||||
.debug_args = {static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), 0},
|
||||
.debug_data = static_cast<uint64_t>(flip_arg),
|
||||
.completion = EndOfPipeCompletion::Flip,
|
||||
.completion_data = request_id,
|
||||
});
|
||||
(void)buffer.Handle();
|
||||
buffer.SetDebugInfo(static_cast<uint32_t>(CommandBufferDebugOp::EopOnlyFlip), submit_id,
|
||||
static_cast<uint32_t>(handle), static_cast<uint32_t>(index),
|
||||
static_cast<uint32_t>(flip_mode), 0, static_cast<uint64_t>(flip_arg));
|
||||
|
||||
auto& renderer = buffer.GetContext();
|
||||
auto& scheduler = renderer.GetCommandScheduler();
|
||||
EXIT_IF(!scheduler.Active() || &buffer != &scheduler.Current());
|
||||
scheduler.DeferPriorityOperation(
|
||||
[&renderer, request_id] { renderer.GetVideoOut().CompleteFlip(request_id); });
|
||||
}
|
||||
|
||||
void TriggerEopEventAtEndOfPipe(CommandBuffer& buffer, int event_id, uint32_t context_id) {
|
||||
ValidateEndOfPipeSignal({.buffer = &buffer});
|
||||
(void)buffer.Handle();
|
||||
auto& renderer = buffer.GetContext();
|
||||
auto& scheduler = renderer.GetCommandScheduler();
|
||||
EXIT_IF(!scheduler.Active() || &buffer != &scheduler.Current());
|
||||
|
||||
@@ -18,37 +18,12 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
struct MemoryStats {
|
||||
std::atomic_uint64_t allocated[VK_MAX_MEMORY_TYPES] {};
|
||||
std::atomic_uint64_t count[VK_MAX_MEMORY_TYPES] {};
|
||||
};
|
||||
|
||||
MemoryStats g_memory_stats;
|
||||
|
||||
} // namespace
|
||||
|
||||
void VulkanTrackAllocation(const VulkanMemory& memory) {
|
||||
g_memory_stats.allocated[memory.type] += memory.requirements.size;
|
||||
g_memory_stats.count[memory.type]++;
|
||||
}
|
||||
|
||||
void VulkanUntrackAllocation(const VulkanMemory& memory) {
|
||||
EXIT_IF(g_memory_stats.allocated[memory.type] < memory.requirements.size);
|
||||
EXIT_IF(g_memory_stats.count[memory.type] == 0);
|
||||
g_memory_stats.allocated[memory.type] -= memory.requirements.size;
|
||||
g_memory_stats.count[memory.type]--;
|
||||
}
|
||||
|
||||
bool GraphicContext::CreateAllocator() {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(instance == nullptr || physical_device == nullptr || device == nullptr ||
|
||||
@@ -163,20 +138,16 @@ bool GraphicContext::CreateImage(const vk::ImageCreateInfo& image_info, VulkanIm
|
||||
alloc_info.preferredFlags =
|
||||
static_cast<vk::MemoryPropertyFlags::MaskType>(memory.preferred_property);
|
||||
|
||||
VmaAllocationInfo allocation_result {};
|
||||
vk::Image::CType native_image = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(
|
||||
vmaCreateImage(allocator, static_cast<const vk::ImageCreateInfo::NativeType*>(image_info),
|
||||
&alloc_info, &native_image, &memory.allocation, &allocation_result));
|
||||
&alloc_info, &native_image, &memory.allocation, nullptr));
|
||||
image.image = native_image;
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LogMemoryBudget();
|
||||
return false;
|
||||
}
|
||||
|
||||
device.getImageMemoryRequirements(image.image, &memory.requirements);
|
||||
memory.type = allocation_result.memoryType;
|
||||
VulkanTrackAllocation(memory);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,7 +156,6 @@ void GraphicContext::DeleteImage(VulkanImage& image) {
|
||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.memory.allocation == nullptr);
|
||||
|
||||
auto& memory = image.memory;
|
||||
VulkanUntrackAllocation(memory);
|
||||
vmaDestroyImage(allocator, image.image, memory.allocation);
|
||||
image.image = nullptr;
|
||||
memory.allocation = nullptr;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_VMA_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_VMA_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
void VulkanTrackAllocation(const VulkanMemory& memory);
|
||||
void VulkanUntrackAllocation(const VulkanMemory& memory);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif /* EMULATOR_SRC_GRAPHICS_HOST_GPU_VMA_H_ */
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/systemOverlay.h"
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/systemOverlay.h"
|
||||
@@ -170,7 +169,12 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
||||
EXIT_NOT_IMPLEMENTED(devices.empty());
|
||||
|
||||
if (Config::GetGpuIndex() >= 0) {
|
||||
devices = {devices[Config::GetGpuIndex()]};
|
||||
if (static_cast<size_t>(Config::GetGpuIndex()) < devices.size()) {
|
||||
devices = {devices[Config::GetGpuIndex()]};
|
||||
} else {
|
||||
LOGF("Vulkan GPU index %d is unavailable; selecting automatically\n",
|
||||
Config::GetGpuIndex());
|
||||
}
|
||||
}
|
||||
|
||||
vk::PhysicalDevice best_device = nullptr;
|
||||
@@ -1116,6 +1120,18 @@ void WindowContext::CreateVulkan() {
|
||||
|
||||
LOGF("Select device: %s\n", device_properties.deviceName.data());
|
||||
|
||||
const vk::PhysicalDeviceImageFormatInfo2 block_texel_view_info {
|
||||
.format = vk::Format::eBc1RgbaUnormBlock,
|
||||
.type = vk::ImageType::e2D,
|
||||
.tiling = vk::ImageTiling::eOptimal,
|
||||
.usage = vk::ImageUsageFlagBits::eSampled,
|
||||
.flags = vk::ImageCreateFlagBits::eBlockTexelViewCompatible,
|
||||
};
|
||||
const auto block_texel_view_props =
|
||||
graphic_ctx.physical_device.getImageFormatProperties2(block_texel_view_info);
|
||||
graphic_ctx.supports_block_texel_view = block_texel_view_props.result == vk::Result::eSuccess;
|
||||
LOGF("Block Texel View support: %s\n", graphic_ctx.supports_block_texel_view ? "Yes" : "No");
|
||||
|
||||
{
|
||||
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
||||
"vkEnumerateDeviceExtensionProperties",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/systemOverlay.h"
|
||||
|
||||
@@ -415,7 +415,7 @@ struct BindingLayout {
|
||||
|
||||
struct ShaderInfo {
|
||||
static constexpr uint32_t MaxBuffers = 32;
|
||||
static constexpr uint32_t MaxImages = 32;
|
||||
static constexpr uint32_t MaxImages = 64;
|
||||
static constexpr uint32_t MaxSamplers = 32;
|
||||
static constexpr uint32_t MaxSampledPairs = 64;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ add_executable(launcher WIN32
|
||||
)
|
||||
|
||||
|
||||
target_link_libraries(launcher Qt6::Concurrent Qt6::Network Qt6::Widgets kyty_git_version)
|
||||
target_link_libraries(launcher Qt6::Concurrent Qt6::Network Qt6::Widgets Vulkan::Headers kyty_git_version)
|
||||
if(WIN32)
|
||||
target_link_libraries(launcher setupapi)
|
||||
endif()
|
||||
|
||||
@@ -127,13 +127,23 @@
|
||||
<property name="toolTip"><string>Optimize shaders for code size or performance</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_gpu">
|
||||
<property name="text"><string>GPU:</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="comboBox_gpu">
|
||||
<property name="toolTip"><string>Graphics device used by the emulator</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_fullscreen">
|
||||
<property name="toolTip"><string>Run in borderless desktop fullscreen</string></property>
|
||||
<property name="text"><string>Fullscreen</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_readback">
|
||||
<property name="toolTip"><string>Read back writable linear images</string></property>
|
||||
<property name="text"><string>Enable readback</string></property>
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
QString user_name = "Kyty";
|
||||
int user_id = Config::DEFAULT_USER_ID;
|
||||
PresentMode present_mode = PresentMode::Fifo;
|
||||
int gpu_index = -1;
|
||||
bool fullscreen_enabled = false;
|
||||
bool readback_linear_images = false;
|
||||
int vblank_frequency = 60;
|
||||
@@ -121,6 +122,7 @@ public:
|
||||
user_name = other.user_name;
|
||||
user_id = other.user_id;
|
||||
present_mode = other.present_mode;
|
||||
gpu_index = other.gpu_index;
|
||||
fullscreen_enabled = other.fullscreen_enabled;
|
||||
readback_linear_images = other.readback_linear_images;
|
||||
vblank_frequency = other.vblank_frequency;
|
||||
@@ -165,6 +167,7 @@ public:
|
||||
KYTY_CFG_SET(user_name);
|
||||
KYTY_CFG_SET(user_id);
|
||||
KYTY_CFG_SET(present_mode);
|
||||
KYTY_CFG_SET(gpu_index);
|
||||
KYTY_CFG_SET(fullscreen_enabled);
|
||||
KYTY_CFG_SET(readback_linear_images);
|
||||
KYTY_CFG_SET(vblank_frequency);
|
||||
@@ -200,6 +203,7 @@ public:
|
||||
? saved_user_id
|
||||
: Config::DEFAULT_USER_ID;
|
||||
KYTY_CFG_GET(present_mode);
|
||||
gpu_index = s->value("gpu_index", -1).toInt();
|
||||
if (EnumToText(present_mode).isEmpty()) {
|
||||
present_mode = PresentMode::Fifo;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QAbstractItemView>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
@@ -27,6 +28,11 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#endif
|
||||
|
||||
#include "ui_configuration_edit_dialog.h"
|
||||
|
||||
constexpr char SETTINGS_CFG_DIALOG[] = "ConfigurationEditDialog";
|
||||
@@ -163,6 +169,38 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
m_ui->spinBox_user_id->setValue(info.user_id);
|
||||
ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution);
|
||||
ListInit(m_ui->comboBox_present_mode, info.present_mode);
|
||||
m_ui->comboBox_gpu->clear();
|
||||
m_ui->comboBox_gpu->addItem(tr("Auto"));
|
||||
// Keep Auto when Qt is built without Vulkan support.
|
||||
#if QT_CONFIG(vulkan)
|
||||
#if defined(__APPLE__)
|
||||
if (!qEnvironmentVariableIsSet("QT_VULKAN_LIB")) {
|
||||
const auto base = QCoreApplication::applicationDirPath();
|
||||
auto library = base + "/libMoltenVK.dylib";
|
||||
if (!QFileInfo::exists(library)) {
|
||||
library = base + "/../Frameworks/libMoltenVK.dylib";
|
||||
}
|
||||
if (QFileInfo::exists(library)) {
|
||||
qputenv("QT_VULKAN_LIB", library.toUtf8());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
QVulkanInstance instance;
|
||||
instance.setApiVersion(QVersionNumber(1, 3, 0));
|
||||
#if !defined(__APPLE__)
|
||||
instance.setFlags(QVulkanInstance::NoPortabilityDrivers);
|
||||
#endif
|
||||
if (instance.create()) {
|
||||
QVulkanWindow window;
|
||||
window.setVulkanInstance(&instance);
|
||||
for (const auto& device: window.availablePhysicalDevices()) {
|
||||
m_ui->comboBox_gpu->addItem(QString::fromUtf8(device.deviceName));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_ui->comboBox_gpu->setCurrentIndex(
|
||||
info.gpu_index >= 0 && info.gpu_index < m_ui->comboBox_gpu->count() - 1 ? info.gpu_index + 1
|
||||
: 0);
|
||||
m_ui->checkBox_fullscreen->setChecked(info.fullscreen_enabled);
|
||||
m_ui->checkBox_readback->setChecked(info.readback_linear_images);
|
||||
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
|
||||
@@ -306,6 +344,7 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
|
||||
TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText());
|
||||
info.present_mode =
|
||||
TextToEnum<Configuration::PresentMode>(ui.comboBox_present_mode->currentText());
|
||||
info.gpu_index = ui.comboBox_gpu->currentIndex() - 1;
|
||||
info.fullscreen_enabled = ui.checkBox_fullscreen->isChecked();
|
||||
info.readback_linear_images = ui.checkBox_readback->isChecked();
|
||||
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
|
||||
|
||||
@@ -229,6 +229,9 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
|
||||
args << "--user-name" << info.user_name;
|
||||
args << "--user-id" << QString::number(info.user_id);
|
||||
args << "--present-mode" << EnumToText(info.present_mode);
|
||||
if (info.gpu_index >= 0) {
|
||||
args << "--gpu" << QString::number(info.gpu_index);
|
||||
}
|
||||
if (info.fullscreen_enabled) {
|
||||
args << "--fullscreen";
|
||||
}
|
||||
|
||||
+47
-37
@@ -105,7 +105,7 @@ private:
|
||||
Format format = Format::Unknown;
|
||||
uint64_t last_output_time = 0;
|
||||
int channels_num = 0;
|
||||
int volume[8] = {};
|
||||
int volume[12] = {};
|
||||
|
||||
SDL_AudioDeviceID audio_device = 0;
|
||||
SDL_AudioSpec audio_spec = {};
|
||||
@@ -127,7 +127,7 @@ private:
|
||||
static bool FormatIsFloat(Format format);
|
||||
static bool FormatIsStd(Format format);
|
||||
static uint32_t BytesPerSample(Format format);
|
||||
static uint32_t FrameSize(const PortOut& port);
|
||||
static uint32_t OutputChannels(const PortOut& port);
|
||||
static SDL_AudioFormat SdlFormat(Format format);
|
||||
static bool OpenSdlDevice(PortOut* port);
|
||||
static void CloseSdlDevice(PortOut* port);
|
||||
@@ -201,8 +201,14 @@ Audio::~Audio() {
|
||||
}
|
||||
|
||||
bool Audio::FormatIsFloat(Format format) {
|
||||
return (format == Format::FloatMono || format == Format::FloatStereo ||
|
||||
format == Format::Float8Ch || format == Format::Float8ChStd);
|
||||
switch (format) {
|
||||
case Format::FloatMono:
|
||||
case Format::FloatStereo:
|
||||
case Format::Float8Ch:
|
||||
case Format::Float8ChStd:
|
||||
case Format::Float12Ch: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::FormatIsStd(Format format) {
|
||||
@@ -213,8 +219,9 @@ uint32_t Audio::BytesPerSample(Format format) {
|
||||
return FormatIsFloat(format) ? sizeof(float) : sizeof(int16_t);
|
||||
}
|
||||
|
||||
uint32_t Audio::FrameSize(const PortOut& port) {
|
||||
return BytesPerSample(port.format) * port.channels_num;
|
||||
uint32_t Audio::OutputChannels(const PortOut& port) {
|
||||
// SDL only takes up to 8 channels. Keep the guest buffer's channel count separate.
|
||||
return std::min(port.channels_num, 8);
|
||||
}
|
||||
|
||||
SDL_AudioFormat Audio::SdlFormat(Format format) {
|
||||
@@ -232,7 +239,7 @@ bool Audio::OpenSdlDevice(PortOut* port) {
|
||||
SDL_AudioSpec desired {};
|
||||
desired.freq = static_cast<int>(port->freq);
|
||||
desired.format = SdlFormat(port->format);
|
||||
desired.channels = static_cast<Uint8>(port->channels_num);
|
||||
desired.channels = static_cast<Uint8>(OutputChannels(*port));
|
||||
desired.samples = static_cast<Uint16>(port->samples_num);
|
||||
desired.callback = nullptr;
|
||||
|
||||
@@ -272,8 +279,9 @@ const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||
|
||||
const auto frames = port.samples_num;
|
||||
const auto channels = static_cast<uint32_t>(port.channels_num);
|
||||
const auto output_channels = OutputChannels(port);
|
||||
const auto bytes_per_sample = BytesPerSample(port.format);
|
||||
const auto src_size = frames * channels * bytes_per_sample;
|
||||
const bool reorder = channels >= 8 && !FormatIsStd(port.format);
|
||||
|
||||
bool volume_changed = false;
|
||||
for (uint32_t ch = 0; ch < channels; ch++) {
|
||||
@@ -283,24 +291,35 @@ const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||
}
|
||||
}
|
||||
|
||||
if (!volume_changed && !FormatIsStd(port.format)) {
|
||||
if (!volume_changed && !reorder) {
|
||||
return data;
|
||||
}
|
||||
|
||||
buffer->resize(src_size);
|
||||
buffer->resize(frames * output_channels * bytes_per_sample);
|
||||
|
||||
static constexpr uint32_t STD_8CH_MAP[8] = {0, 1, 2, 3, 6, 7, 4, 5};
|
||||
// SDL wants back speakers before side speakers; non-STD PCM has them reversed.
|
||||
static constexpr uint32_t SDL_8CH_MAP[8] = {0, 1, 2, 3, 6, 7, 4, 5};
|
||||
|
||||
if (FormatIsFloat(port.format)) {
|
||||
auto* dst = reinterpret_cast<float*>(buffer->data());
|
||||
const auto* src = static_cast<const float*>(data);
|
||||
|
||||
for (uint32_t frame = 0; frame < frames; frame++) {
|
||||
for (uint32_t ch = 0; ch < channels; ch++) {
|
||||
const auto src_ch =
|
||||
(FormatIsStd(port.format) && channels == 8 ? STD_8CH_MAP[ch] : ch);
|
||||
dst[frame * channels + ch] = src[frame * channels + src_ch] *
|
||||
(static_cast<float>(port.volume[ch]) / 32768.0f);
|
||||
for (uint32_t ch = 0; ch < output_channels; ch++) {
|
||||
const auto src_ch = reorder ? SDL_8CH_MAP[ch] : ch;
|
||||
dst[frame * output_channels + ch] =
|
||||
src[frame * channels + src_ch] *
|
||||
(static_cast<float>(port.volume[src_ch]) / 32768.0f);
|
||||
}
|
||||
if (channels == 12) {
|
||||
// Add the four top channels to their front/back channels, turning 12 into 8.
|
||||
// SDL handles the rest if the output device has fewer channels.
|
||||
static constexpr uint32_t HEIGHT_DST[4] = {0, 1, 4, 5};
|
||||
for (uint32_t ch = 0; ch < 4; ch++) {
|
||||
dst[frame * output_channels + HEIGHT_DST[ch]] +=
|
||||
src[frame * channels + 8 + ch] *
|
||||
(static_cast<float>(port.volume[8 + ch]) / 32768.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -308,17 +327,16 @@ const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||
const auto* src = static_cast<const int16_t*>(data);
|
||||
|
||||
for (uint32_t frame = 0; frame < frames; frame++) {
|
||||
for (uint32_t ch = 0; ch < channels; ch++) {
|
||||
const auto src_ch =
|
||||
(FormatIsStd(port.format) && channels == 8 ? STD_8CH_MAP[ch] : ch);
|
||||
for (uint32_t ch = 0; ch < output_channels; ch++) {
|
||||
const auto src_ch = reorder ? SDL_8CH_MAP[ch] : ch;
|
||||
int64_t sample =
|
||||
static_cast<int64_t>(src[frame * channels + src_ch]) * port.volume[ch] / 32768;
|
||||
static_cast<int64_t>(src[frame * channels + src_ch]) * port.volume[src_ch] / 32768;
|
||||
if (sample > std::numeric_limits<int16_t>::max()) {
|
||||
sample = std::numeric_limits<int16_t>::max();
|
||||
} else if (sample < std::numeric_limits<int16_t>::min()) {
|
||||
sample = std::numeric_limits<int16_t>::min();
|
||||
}
|
||||
dst[frame * channels + ch] = static_cast<int16_t>(sample);
|
||||
dst[frame * output_channels + ch] = static_cast<int16_t>(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,8 +352,10 @@ bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
||||
}
|
||||
|
||||
std::vector<uint8_t> prepared_buffer;
|
||||
const void* prepared_data = PrepareOutputBuffer(*port, data, &prepared_buffer);
|
||||
const auto prepared_size = FrameSize(*port) * port->samples_num;
|
||||
const void* prepared_data = PrepareOutputBuffer(*port, data, &prepared_buffer);
|
||||
const auto output_channels = OutputChannels(*port);
|
||||
const auto prepared_size =
|
||||
BytesPerSample(port->format) * output_channels * port->samples_num;
|
||||
|
||||
std::vector<uint8_t> convert_buffer;
|
||||
const void* queue_data = prepared_data;
|
||||
@@ -343,7 +363,7 @@ bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
||||
|
||||
SDL_AudioCVT cvt {};
|
||||
const int cvt_result =
|
||||
SDL_BuildAudioCVT(&cvt, SdlFormat(port->format), static_cast<Uint8>(port->channels_num),
|
||||
SDL_BuildAudioCVT(&cvt, SdlFormat(port->format), static_cast<Uint8>(output_channels),
|
||||
static_cast<int>(port->freq), port->audio_spec.format,
|
||||
port->audio_spec.channels, port->audio_spec.freq);
|
||||
|
||||
@@ -416,6 +436,7 @@ Audio::Id Audio::AudioOutOpen(int type, uint32_t samples_num, uint32_t freq, For
|
||||
case Format::Float8Ch:
|
||||
case Format::Signed16bit8ChStd:
|
||||
case Format::Float8ChStd: port.channels_num = 8; break;
|
||||
case Format::Float12Ch: port.channels_num = 12; break;
|
||||
default: EXIT("unknown format");
|
||||
}
|
||||
|
||||
@@ -488,20 +509,9 @@ bool Audio::AudioOutSetVolume(Id handle, uint32_t bitflag, const int* volume) {
|
||||
auto bit = bitflag & 0x1u;
|
||||
|
||||
if (bit == 1) {
|
||||
int src_index = i;
|
||||
if (port.format == Format::Float8ChStd ||
|
||||
port.format == Format::Signed16bit8ChStd) {
|
||||
switch (i) {
|
||||
case 4: src_index = 6; break;
|
||||
case 5: src_index = 7; break;
|
||||
case 6: src_index = 4; break;
|
||||
case 7: src_index = 5; break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
port.volume[i] = volume[src_index];
|
||||
port.volume[i] = volume[i];
|
||||
|
||||
LOGF("\t port.volume[%d] = volume[%d] (%d)\n", i, src_index, volume[src_index]);
|
||||
LOGF("\t port.volume[%d] = volume[%d] (%d)\n", i, i, volume[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ enum class Format {
|
||||
Float8Ch,
|
||||
Signed16bit8ChStd,
|
||||
Float8ChStd,
|
||||
Float12Ch,
|
||||
};
|
||||
|
||||
struct OutputParam {
|
||||
|
||||
@@ -261,6 +261,11 @@ static AudioInternal::Format audioout2_data_format_to_audio_format(uint32_t data
|
||||
case 8:
|
||||
return is_std ? AudioInternal::Format::Float8ChStd
|
||||
: AudioInternal::Format::Float8Ch;
|
||||
case 12:
|
||||
if (!is_std) {
|
||||
return AudioInternal::Format::Float12Ch;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
+29
-1
@@ -1414,13 +1414,41 @@ namespace LibNpCommerce {
|
||||
|
||||
LIB_VERSION("NpCommerce", 1, "NpCommerce", 1, 1);
|
||||
|
||||
constexpr int COMMERCE_STATUS_NONE = 0;
|
||||
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
|
||||
|
||||
constexpr int COMMERCE_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
|
||||
constexpr int COMMERCE_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004u);
|
||||
|
||||
static int g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogInitialize() {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status != COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_INITIALIZED;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogTerminate() {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status == COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
|
||||
PRINT_NAME();
|
||||
|
||||
return 0; // SCE_COMMON_DIALOG_STATUS_NONE
|
||||
return g_commerce_status;
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitNet_1_NpCommerce) {
|
||||
LIB_FUNC("0aR2aWmQal4", NpCommerceDialogInitialize);
|
||||
LIB_FUNC("m-I92Ab50W8", NpCommerceDialogTerminate);
|
||||
LIB_FUNC("LR5cwFMMCVE", NpCommerceDialogUpdateStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,31 @@ void TestSynchronousDevicePushBypassesModelledQueue() {
|
||||
AudioOut2::AudioOut2ContextDestroy(context);
|
||||
}
|
||||
|
||||
void TestFloat12ChannelPortOutputsPcm() {
|
||||
const auto context = CreateContext();
|
||||
const auto param = MakeParam(0x0c00);
|
||||
AudioOut2::AudioOut2PortHandle port = 0;
|
||||
Check(AudioOut2::AudioOut2PortCreate(context, AsParam(¶m), &port) == OK,
|
||||
"12-channel port create failed");
|
||||
Check(LiveDeviceCount() == 1, "12-channel port did not open an audio device");
|
||||
|
||||
PortState state {};
|
||||
Check(AudioOut2::AudioOut2PortGetState(port, AsState(&state)) == OK,
|
||||
"12-channel port state query failed");
|
||||
Check(state.num_channels == 12, "12-channel port lost its guest channel count");
|
||||
|
||||
float pcm[512 * 12] {};
|
||||
SetPcm(port, pcm);
|
||||
ResetOutputCalls();
|
||||
Check(AudioOut2::AudioOut2ContextPush(context, 1) == OK, "12-channel PCM push failed");
|
||||
const auto calls = OutputCalls();
|
||||
Check(calls.size() == 1 && calls[0], "12-channel PCM did not reach the device backend");
|
||||
|
||||
AudioOut2::AudioOut2PortDestroy(port);
|
||||
Check(LiveDeviceCount() == 0, "12-channel port leaked its audio device");
|
||||
AudioOut2::AudioOut2ContextDestroy(context);
|
||||
}
|
||||
|
||||
void TestAsynchronousDevicePushKeepsQueueBounded() {
|
||||
const auto context = CreateContext(1);
|
||||
const auto param = MakeParam();
|
||||
@@ -361,6 +386,7 @@ int main() {
|
||||
TestConcurrentCreates();
|
||||
TestContextDestroyCancelsPendingCreate();
|
||||
TestSynchronousDevicePushBypassesModelledQueue();
|
||||
TestFloat12ChannelPortOutputsPcm();
|
||||
TestAsynchronousDevicePushKeepsQueueBounded();
|
||||
TestHandleWithoutPcmDoesNotBypassQueue();
|
||||
std::printf("AudioOut2PortTests: all cases passed\n");
|
||||
|
||||
@@ -1155,9 +1155,8 @@ void TestNativeShaderResourceDependencies() {
|
||||
Check(HasShaderBufferWrites(runtime),
|
||||
"graphics/compute write predicate lost a maximum-size strided buffer");
|
||||
|
||||
VulkanBuffer buffer;
|
||||
buffer.buffer = reinterpret_cast<vk::Buffer::CType>(uintptr_t{1});
|
||||
const auto gds_barrier = MakeGdsDependency(buffer.buffer);
|
||||
const vk::Buffer buffer{reinterpret_cast<vk::Buffer::CType>(uintptr_t{1})};
|
||||
const auto gds_barrier = MakeGdsDependency(buffer);
|
||||
Check((gds_barrier.srcAccessMask & vk::AccessFlagBits::eHostWrite) &&
|
||||
(gds_barrier.srcAccessMask & vk::AccessFlagBits::eTransferWrite) &&
|
||||
(gds_barrier.srcAccessMask & vk::AccessFlagBits::eShaderWrite) &&
|
||||
|
||||
Reference in new issue
Block a user