feat(video): add resizable aspect-ratio-aware window
This commit is contained in:
parent
b1722c06e3
commit
282257fd98
|
|
@ -12,8 +12,7 @@ Execution reaches `_start`, [stack unwinding](core/libs/prx/libc/src/exception/U
|
|||
|
||||
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces validated via [Spirv-Tools](3rdparty/SPIRV-Tools) SPIR-V.
|
||||
|
||||
The application reaches the render loop and **successfully presents its first frame**: the application logo is visible on screen.
|
||||
|
||||
The application reaches the render loop and **successfully presents its first frame** (logo).
|
||||
Current exception: `AGC graphics: unsupported primitive type 17`.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ add_library(libSceAgcDriver SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/BdaFeatures.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Pm4.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/VulkanDevice.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/AspectFit.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/PresentationScaler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Draw.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/ColorTargetLayout.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/ColorTargetTransfer.cpp
|
||||
|
|
@ -279,6 +281,8 @@ if(AGC_BUILD_VISUAL_TEST)
|
|||
add_executable(agc_driver_visual_test EXCLUDE_FROM_ALL
|
||||
tests/Visual.cpp
|
||||
Execution/src/VulkanDevice.cpp
|
||||
Execution/src/AspectFit.cpp
|
||||
Execution/src/PresentationScaler.cpp
|
||||
Execution/src/GuestMemory.cpp
|
||||
Execution/src/BdaFeatures.cpp
|
||||
Graphics/src/Draw.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_ASPECTFIT_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_ASPECTFIT_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace AgcDriver {
|
||||
|
||||
struct AspectFitRect {
|
||||
std::int32_t x;
|
||||
std::int32_t y;
|
||||
std::uint32_t width;
|
||||
std::uint32_t height;
|
||||
};
|
||||
|
||||
struct AspectFitSize {
|
||||
std::uint32_t width;
|
||||
std::uint32_t height;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
double ComputeContainScale_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t boundsWidth, std::uint32_t boundsHeight, bool allowUpscale);
|
||||
AspectFitSize ComputeContainSize_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t boundsWidth, std::uint32_t boundsHeight, bool allowUpscale);
|
||||
AspectFitRect ComputeContainRect_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t destinationWidth, std::uint32_t destinationHeight);
|
||||
std::uint32_t ComputeWidthForHeight_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t height);
|
||||
std::uint32_t ComputeHeightForWidth_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t width);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -14,6 +14,7 @@ struct PresentationWindow {
|
|||
void* context;
|
||||
std::span<const char* const> extensions;
|
||||
VkSurfaceKHR (*createSurface)(void* context, VkInstance instance);
|
||||
void (*getDrawableSize)(void* context, std::uint32_t* width, std::uint32_t* height);
|
||||
std::uint32_t width;
|
||||
std::uint32_t height;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PRESENTATIONSCALER_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PRESENTATIONSCALER_HPP
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Context.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace AgcDriver {
|
||||
|
||||
class PresentationScaler {
|
||||
public:
|
||||
PresentationScaler(const Graphics::Context& context, VkFormat sourceFormat, VkFormat destinationFormat);
|
||||
~PresentationScaler();
|
||||
PresentationScaler(const PresentationScaler&) = delete;
|
||||
PresentationScaler& operator=(const PresentationScaler&) = delete;
|
||||
|
||||
void EnsureSourceImage(std::uint32_t width, std::uint32_t height);
|
||||
void RecordUpload(VkCommandBuffer commands, VkBuffer uploadBuffer);
|
||||
void RecordBlit(VkCommandBuffer commands, VkImage destinationImage, std::uint32_t destinationWidth, std::uint32_t destinationHeight);
|
||||
|
||||
private:
|
||||
void release() noexcept;
|
||||
|
||||
Graphics::Context context;
|
||||
VkFormat sourceFormat;
|
||||
std::uint32_t sourceWidth = 0;
|
||||
std::uint32_t sourceHeight = 0;
|
||||
VkImage sourceImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory sourceMemory = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -20,6 +20,7 @@ public:
|
|||
void WaitIdle();
|
||||
void* Window() const;
|
||||
void Resize(std::uint32_t width, std::uint32_t height);
|
||||
bool Presentable() const;
|
||||
std::uint64_t PresentClear(std::uint32_t width, std::uint32_t height, bool opaque);
|
||||
std::uint64_t PresentPixels(std::uint32_t width, std::uint32_t height, std::span<const std::byte> pixels);
|
||||
void WaitPresented(std::uint64_t id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/AspectFit.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace AgcDriver {
|
||||
namespace {
|
||||
|
||||
void require(bool condition, const char* reason) {
|
||||
if (!condition) throw std::runtime_error(std::string("AspectFit: ") + reason);
|
||||
}
|
||||
|
||||
std::uint32_t roundToExtent(double value) {
|
||||
require(value >= 0.0 && value <= static_cast<double>(std::numeric_limits<std::uint32_t>::max()), "computed extent is out of range");
|
||||
return static_cast<std::uint32_t>(std::lround(value));
|
||||
}
|
||||
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
double ComputeContainScale_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t boundsWidth, std::uint32_t boundsHeight, bool allowUpscale) {
|
||||
require(sourceWidth != 0 && sourceHeight != 0, "source extent must be non-zero");
|
||||
require(boundsWidth != 0 && boundsHeight != 0, "bounds extent must be non-zero");
|
||||
const auto scale = std::min(static_cast<double>(boundsWidth) / static_cast<double>(sourceWidth), static_cast<double>(boundsHeight) / static_cast<double>(sourceHeight));
|
||||
return allowUpscale ? scale : std::min(scale, 1.0);
|
||||
}
|
||||
|
||||
AspectFitSize ComputeContainSize_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t boundsWidth, std::uint32_t boundsHeight, bool allowUpscale) {
|
||||
const auto scale = ComputeContainScale_nid_postfix(sourceWidth, sourceHeight, boundsWidth, boundsHeight, allowUpscale);
|
||||
return AspectFitSize{std::max<std::uint32_t>(1, roundToExtent(sourceWidth * scale)), std::max<std::uint32_t>(1, roundToExtent(sourceHeight * scale))};
|
||||
}
|
||||
|
||||
AspectFitRect ComputeContainRect_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t destinationWidth, std::uint32_t destinationHeight) {
|
||||
const auto size = ComputeContainSize_nid_postfix(sourceWidth, sourceHeight, destinationWidth, destinationHeight, true);
|
||||
const auto x = (static_cast<std::int64_t>(destinationWidth) - static_cast<std::int64_t>(size.width)) / 2;
|
||||
const auto y = (static_cast<std::int64_t>(destinationHeight) - static_cast<std::int64_t>(size.height)) / 2;
|
||||
return AspectFitRect{static_cast<std::int32_t>(x), static_cast<std::int32_t>(y), size.width, size.height};
|
||||
}
|
||||
|
||||
std::uint32_t ComputeWidthForHeight_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t height) {
|
||||
require(sourceWidth != 0 && sourceHeight != 0, "source extent must be non-zero");
|
||||
return std::max<std::uint32_t>(1, roundToExtent(static_cast<double>(height) * sourceWidth / sourceHeight));
|
||||
}
|
||||
|
||||
std::uint32_t ComputeHeightForWidth_nid_postfix(std::uint32_t sourceWidth, std::uint32_t sourceHeight, std::uint32_t width) {
|
||||
require(sourceWidth != 0 && sourceHeight != 0, "source extent must be non-zero");
|
||||
return std::max<std::uint32_t>(1, roundToExtent(static_cast<double>(width) * sourceHeight / sourceWidth));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -186,8 +186,10 @@ public:
|
|||
void Present(const PresentationWindow& window, const DisplayBuffer* buffer, bool opaque, void (*gpuReady)(void*), void* context) {
|
||||
CheckFailure();
|
||||
require(gpuReady != nullptr && context != nullptr, "missing GPU completion callback");
|
||||
require(window.getDrawableSize != nullptr, "missing window drawable size query");
|
||||
std::shared_ptr<VulkanDevice> presenting;
|
||||
std::uint64_t id = 0;
|
||||
bool presented = false;
|
||||
try {
|
||||
{
|
||||
std::lock_guard lock(gpuMutex);
|
||||
|
|
@ -197,18 +199,24 @@ public:
|
|||
}
|
||||
require(device->Window() == window.context, "presentation window does not match device surface");
|
||||
presenting = device;
|
||||
presenting->Resize(window.width, window.height);
|
||||
if (buffer != nullptr) {
|
||||
require(buffer->width == window.width && buffer->height == window.height, "display buffer extent differs from output");
|
||||
presenting->WaitIdle();
|
||||
const auto pixels = ReadDisplayBuffer(*buffer);
|
||||
id = presenting->PresentPixels(window.width, window.height, pixels);
|
||||
} else {
|
||||
id = presenting->PresentClear(window.width, window.height, opaque);
|
||||
std::uint32_t drawableWidth = 0;
|
||||
std::uint32_t drawableHeight = 0;
|
||||
window.getDrawableSize(window.context, &drawableWidth, &drawableHeight);
|
||||
presenting->Resize(drawableWidth, drawableHeight);
|
||||
if (presenting->Presentable()) {
|
||||
if (buffer != nullptr) {
|
||||
require(buffer->width == window.width && buffer->height == window.height, "display buffer extent differs from output");
|
||||
presenting->WaitIdle();
|
||||
const auto pixels = ReadDisplayBuffer(*buffer);
|
||||
id = presenting->PresentPixels(window.width, window.height, pixels);
|
||||
} else {
|
||||
id = presenting->PresentClear(window.width, window.height, opaque);
|
||||
}
|
||||
presented = true;
|
||||
}
|
||||
}
|
||||
gpuReady(context);
|
||||
presenting->WaitPresented(id);
|
||||
if (presented) presenting->WaitPresented(id);
|
||||
CheckFailure();
|
||||
} catch (...) {
|
||||
ReportFailure(std::current_exception());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/PresentationScaler.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/AspectFit.hpp"
|
||||
|
||||
namespace AgcDriver {
|
||||
|
||||
PresentationScaler::PresentationScaler(const Graphics::Context& context, VkFormat sourceFormat, VkFormat destinationFormat) : context(context), sourceFormat(sourceFormat) {
|
||||
Graphics::Require(context.formatProperties != nullptr, "missing Vulkan format property resolver");
|
||||
VkFormatProperties sourceProperties{};
|
||||
context.formatProperties(context.physical, sourceFormat, &sourceProperties);
|
||||
Graphics::Require((sourceProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0, "presentation source format does not support blit sources");
|
||||
Graphics::Require((sourceProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0, "presentation source format does not support linear blit filtering");
|
||||
VkFormatProperties destinationProperties{};
|
||||
context.formatProperties(context.physical, destinationFormat, &destinationProperties);
|
||||
Graphics::Require((destinationProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0, "presentation destination format does not support blit targets");
|
||||
}
|
||||
|
||||
PresentationScaler::~PresentationScaler() {
|
||||
release();
|
||||
}
|
||||
|
||||
void PresentationScaler::release() noexcept {
|
||||
if (sourceImage != VK_NULL_HANDLE) context.Function<PFN_vkDestroyImage>("vkDestroyImage")(context.device, sourceImage, nullptr);
|
||||
if (sourceMemory != VK_NULL_HANDLE) context.Function<PFN_vkFreeMemory>("vkFreeMemory")(context.device, sourceMemory, nullptr);
|
||||
sourceImage = VK_NULL_HANDLE;
|
||||
sourceMemory = VK_NULL_HANDLE;
|
||||
sourceWidth = 0;
|
||||
sourceHeight = 0;
|
||||
}
|
||||
|
||||
void PresentationScaler::EnsureSourceImage(std::uint32_t width, std::uint32_t height) {
|
||||
Graphics::Require(width != 0 && height != 0, "presentation source extent must be non-zero");
|
||||
if (sourceImage != VK_NULL_HANDLE && sourceWidth == width && sourceHeight == height) return;
|
||||
release();
|
||||
VkImageCreateInfo imageInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = sourceFormat;
|
||||
imageInfo.extent = {width, height, 1u};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
Graphics::Check(context.Function<PFN_vkCreateImage>("vkCreateImage")(context.device, &imageInfo, nullptr, &sourceImage), "vkCreateImage presentation source");
|
||||
VkMemoryRequirements requirements{};
|
||||
context.Function<PFN_vkGetImageMemoryRequirements>("vkGetImageMemoryRequirements")(context.device, sourceImage, &requirements);
|
||||
VkMemoryAllocateInfo allocation{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
|
||||
allocation.allocationSize = requirements.size;
|
||||
allocation.memoryTypeIndex = context.MemoryType(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
Graphics::Check(context.Function<PFN_vkAllocateMemory>("vkAllocateMemory")(context.device, &allocation, nullptr, &sourceMemory), "vkAllocateMemory presentation source");
|
||||
Graphics::Check(context.Function<PFN_vkBindImageMemory>("vkBindImageMemory")(context.device, sourceImage, sourceMemory, 0), "vkBindImageMemory presentation source");
|
||||
sourceWidth = width;
|
||||
sourceHeight = height;
|
||||
}
|
||||
|
||||
void PresentationScaler::RecordUpload(VkCommandBuffer commands, VkBuffer uploadBuffer) {
|
||||
Graphics::Require(sourceImage != VK_NULL_HANDLE, "presentation source image has not been created");
|
||||
const auto pipelineBarrier = context.Function<PFN_vkCmdPipelineBarrier>("vkCmdPipelineBarrier");
|
||||
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = sourceImage;
|
||||
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
pipelineBarrier(commands, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
VkBufferImageCopy copy{};
|
||||
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
copy.imageExtent = {sourceWidth, sourceHeight, 1u};
|
||||
context.Function<PFN_vkCmdCopyBufferToImage>("vkCmdCopyBufferToImage")(commands, uploadBuffer, sourceImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
pipelineBarrier(commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
void PresentationScaler::RecordBlit(VkCommandBuffer commands, VkImage destinationImage, std::uint32_t destinationWidth, std::uint32_t destinationHeight) {
|
||||
Graphics::Require(sourceImage != VK_NULL_HANDLE, "presentation source image has not been created");
|
||||
const auto rect = ComputeContainRect_nid_postfix(sourceWidth, sourceHeight, destinationWidth, destinationHeight);
|
||||
VkImageBlit blit{};
|
||||
blit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
blit.srcOffsets[0] = {0, 0, 0};
|
||||
blit.srcOffsets[1] = {static_cast<std::int32_t>(sourceWidth), static_cast<std::int32_t>(sourceHeight), 1};
|
||||
blit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
blit.dstOffsets[0] = {rect.x, rect.y, 0};
|
||||
blit.dstOffsets[1] = {rect.x + static_cast<std::int32_t>(rect.width), rect.y + static_cast<std::int32_t>(rect.height), 1};
|
||||
context.Function<PFN_vkCmdBlitImage>("vkCmdBlitImage")(commands, sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destinationImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#include "BdaAbi.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/PresentationScaler.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <SDL_loadso.h>
|
||||
|
|
@ -68,6 +69,7 @@ struct VulkanDevice::State {
|
|||
bool textureCompressionBC = false;
|
||||
std::unique_ptr<Graphics::TextureDetiler> detiler;
|
||||
VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT};
|
||||
std::unique_ptr<PresentationScaler> scaler;
|
||||
|
||||
template<typename TFunction>
|
||||
TFunction InstanceFunction(const char* name) const {
|
||||
|
|
@ -133,6 +135,7 @@ struct VulkanDevice::State {
|
|||
const auto idle = reinterpret_cast<PFN_vkDeviceWaitIdle>(deviceProc(device, "vkDeviceWaitIdle"))(device);
|
||||
if (idle != VK_SUCCESS && idle != VK_ERROR_DEVICE_LOST) std::terminate();
|
||||
detiler.reset();
|
||||
scaler.reset();
|
||||
if (presentQueued && idle != VK_ERROR_DEVICE_LOST) {
|
||||
const auto result = reinterpret_cast<PFN_vkWaitForFences>(deviceProc(device, "vkWaitForFences"))(device, 1, &presentFence, VK_TRUE, std::numeric_limits<std::uint64_t>::max());
|
||||
if (result != VK_SUCCESS && result != VK_ERROR_DEVICE_LOST) std::terminate();
|
||||
|
|
@ -186,7 +189,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
std::vector<const char*> instanceExtensions;
|
||||
if (window != nullptr) {
|
||||
APS5_LOG_OUT("Presentation window context=%p extent=%ux%u extensions=%zu", window->context, window->width, window->height, window->extensions.size());
|
||||
require(window->context && window->createSurface && window->width && window->height, "invalid window descriptor");
|
||||
require(window->context && window->createSurface && window->getDrawableSize && window->width && window->height, "invalid window descriptor");
|
||||
instanceExtensions.assign(window->extensions.begin(), window->extensions.end());
|
||||
instanceExtensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
|
||||
instanceExtensions.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
||||
|
|
@ -408,12 +411,17 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
check(state->DeviceFunction<PFN_vkCreateCommandPool>("vkCreateCommandPool")(state->device, &poolInfo, nullptr, &state->pool), "vkCreateCommandPool");
|
||||
state->detiler = std::make_unique<Graphics::TextureDetiler>(graphicsContext());
|
||||
if (window != nullptr) {
|
||||
require(window->getDrawableSize != nullptr, "missing window drawable size query");
|
||||
std::uint32_t drawableWidth = 0;
|
||||
std::uint32_t drawableHeight = 0;
|
||||
window->getDrawableSize(window->context, &drawableWidth, &drawableHeight);
|
||||
require(drawableWidth != 0 && drawableHeight != 0, "window has a zero drawable size at creation");
|
||||
VkSurfaceCapabilitiesKHR surface{};
|
||||
check(state->InstanceFunction<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")(selected, state->surface, &surface), "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||
state->extent = {window->width, window->height};
|
||||
APS5_LOG_OUT("Surface capabilities requested=%ux%u min=%ux%u max=%ux%u minImages=%u maxImages=%u usage=0x%x", window->width, window->height, surface.minImageExtent.width, surface.minImageExtent.height, surface.maxImageExtent.width, surface.maxImageExtent.height, surface.minImageCount, surface.maxImageCount, surface.supportedUsageFlags);
|
||||
require(surface.currentExtent.width == std::numeric_limits<std::uint32_t>::max() || (surface.currentExtent.width == window->width && surface.currentExtent.height == window->height), "window extent differs from requested output");
|
||||
require(window->width >= surface.minImageExtent.width && window->width <= surface.maxImageExtent.width && window->height >= surface.minImageExtent.height && window->height <= surface.maxImageExtent.height, "unsupported output extent");
|
||||
state->extent = {drawableWidth, drawableHeight};
|
||||
APS5_LOG_OUT("Surface capabilities drawable=%ux%u min=%ux%u max=%ux%u minImages=%u maxImages=%u usage=0x%x", drawableWidth, drawableHeight, surface.minImageExtent.width, surface.minImageExtent.height, surface.maxImageExtent.width, surface.maxImageExtent.height, surface.minImageCount, surface.maxImageCount, surface.supportedUsageFlags);
|
||||
require(surface.currentExtent.width == std::numeric_limits<std::uint32_t>::max() || (surface.currentExtent.width == drawableWidth && surface.currentExtent.height == drawableHeight), "window extent differs from the real drawable size");
|
||||
require(drawableWidth >= surface.minImageExtent.width && drawableWidth <= surface.maxImageExtent.width && drawableHeight >= surface.minImageExtent.height && drawableHeight <= surface.maxImageExtent.height, "unsupported output extent");
|
||||
require((surface.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0, "surface does not support transfer destination images");
|
||||
require((surface.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0, "opaque composition is unavailable");
|
||||
require((surface.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0, "identity surface transform is unavailable");
|
||||
|
|
@ -455,6 +463,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
allocation.commandBufferCount = 1;
|
||||
check(state->DeviceFunction<PFN_vkAllocateCommandBuffers>("vkAllocateCommandBuffers")(state->device, &allocation, &state->clearCommands), "vkAllocateCommandBuffers");
|
||||
APS5_LOG_OUT("Presentation resources ready acquireFence=%p renderFence=%p presentFence=%p semaphore=%p commands=%p", reinterpret_cast<void*>(state->acquireFence), reinterpret_cast<void*>(state->renderFence), reinterpret_cast<void*>(state->presentFence), reinterpret_cast<void*>(state->rendered), reinterpret_cast<void*>(state->clearCommands));
|
||||
state->scaler = std::make_unique<PresentationScaler>(graphicsContext(), VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -473,6 +482,10 @@ void* VulkanDevice::Window() const {
|
|||
void VulkanDevice::Resize(std::uint32_t width, std::uint32_t height) {
|
||||
APS5_LOG_OUT("Resize requested=%ux%u current=%ux%u presentQueued=%u", width, height, state->extent.width, state->extent.height, static_cast<unsigned>(state->presentQueued));
|
||||
require(state->swapchain != VK_NULL_HANDLE && !state->presentQueued, "cannot resize an unavailable or pending swapchain");
|
||||
if (width == 0 || height == 0) {
|
||||
state->extent = {0, 0};
|
||||
return;
|
||||
}
|
||||
if (state->extent.width == width && state->extent.height == height) return;
|
||||
WaitIdle();
|
||||
VkSurfaceCapabilitiesKHR surface{};
|
||||
|
|
@ -506,6 +519,10 @@ void VulkanDevice::Resize(std::uint32_t width, std::uint32_t height) {
|
|||
APS5_LOG_OUT("Resize complete swapchain=%p extent=%ux%u images=%u", reinterpret_cast<void*>(state->swapchain), state->extent.width, state->extent.height, count);
|
||||
}
|
||||
|
||||
bool VulkanDevice::Presentable() const {
|
||||
return state->extent.width != 0 && state->extent.height != 0;
|
||||
}
|
||||
|
||||
std::uint64_t VulkanDevice::PresentClear(std::uint32_t width, std::uint32_t height, bool opaque) {
|
||||
APS5_LOG_OUT("PresentClear width=%u height=%u opaque=%u", width, height, static_cast<unsigned>(opaque));
|
||||
return present(width, height, opaque, {});
|
||||
|
|
@ -521,7 +538,7 @@ std::uint64_t VulkanDevice::PresentPixels(std::uint32_t width, std::uint32_t hei
|
|||
std::uint64_t VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaque, std::span<const std::byte> pixels) {
|
||||
APS5_LOG_OUT("present begin width=%u height=%u opaque=%u pixels=%zu swapchain=%p extent=%ux%u queued=%u id=%llu", width, height, static_cast<unsigned>(opaque), pixels.size(), reinterpret_cast<void*>(state->swapchain), state->extent.width, state->extent.height, static_cast<unsigned>(state->presentQueued), static_cast<unsigned long long>(state->presentId));
|
||||
require(state->swapchain != VK_NULL_HANDLE, "device has no swapchain");
|
||||
require(width == state->extent.width && height == state->extent.height, "output resize is not implemented");
|
||||
require(state->extent.width != 0 && state->extent.height != 0, "output window is minimized");
|
||||
require(!state->presentQueued, "previous presentation has not completed");
|
||||
require(state->presentId != std::numeric_limits<std::uint64_t>::max(), "presentation ID overflow");
|
||||
if (!pixels.empty()) state->Upload(pixels);
|
||||
|
|
@ -559,11 +576,24 @@ std::uint64_t VulkanDevice::present(std::uint32_t width, std::uint32_t height, b
|
|||
clear.float32[3] = opaque ? 1.0f : 0.0f;
|
||||
state->DeviceFunction<PFN_vkCmdClearColorImage>("vkCmdClearColorImage")(commands, barrier.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear, 1, &barrier.subresourceRange);
|
||||
} else {
|
||||
APS5_LOG_OUT("Recording swapchain upload width=%u height=%u bytes=%zu buffer=%p image=%p", width, height, pixels.size(), reinterpret_cast<void*>(state->uploadBuffer), reinterpret_cast<void*>(barrier.image));
|
||||
VkBufferImageCopy copy{};
|
||||
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
copy.imageExtent = {width, height, 1};
|
||||
state->DeviceFunction<PFN_vkCmdCopyBufferToImage>("vkCmdCopyBufferToImage")(commands, state->uploadBuffer, barrier.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
APS5_LOG_OUT("Recording swapchain scaled blit width=%u height=%u bytes=%zu buffer=%p image=%p", width, height, pixels.size(), reinterpret_cast<void*>(state->uploadBuffer), reinterpret_cast<void*>(barrier.image));
|
||||
require(state->scaler != nullptr, "presentation scaler is unavailable");
|
||||
state->scaler->EnsureSourceImage(width, height);
|
||||
state->scaler->RecordUpload(commands, state->uploadBuffer);
|
||||
VkClearColorValue letterbox{};
|
||||
letterbox.float32[3] = 1.0f;
|
||||
state->DeviceFunction<PFN_vkCmdClearColorImage>("vkCmdClearColorImage")(commands, barrier.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &letterbox, 1, &barrier.subresourceRange);
|
||||
VkImageMemoryBarrier letterboxBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
letterboxBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
letterboxBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
letterboxBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
letterboxBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
letterboxBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
letterboxBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
letterboxBarrier.image = barrier.image;
|
||||
letterboxBarrier.subresourceRange = barrier.subresourceRange;
|
||||
pipelineBarrier(commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &letterboxBarrier);
|
||||
state->scaler->RecordBlit(commands, barrier.image, state->extent.width, state->extent.height);
|
||||
}
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = 0;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@ void Run(SDL_Window* window, const std::filesystem::path& directory, bool verify
|
|||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
Require(SDL_Vulkan_CreateSurface(static_cast<SDL_Window*>(context), instance, &surface) == SDL_TRUE, SDL_GetError());
|
||||
return surface;
|
||||
}, [](void* context, std::uint32_t* width, std::uint32_t* height) {
|
||||
int drawableWidth = 0;
|
||||
int drawableHeight = 0;
|
||||
SDL_Vulkan_GetDrawableSize(static_cast<SDL_Window*>(context), &drawableWidth, &drawableHeight);
|
||||
*width = drawableWidth > 0 ? static_cast<std::uint32_t>(drawableWidth) : 0;
|
||||
*height = drawableHeight > 0 ? static_cast<std::uint32_t>(drawableHeight) : 0;
|
||||
}, Width, Height};
|
||||
AgcDriver::VulkanDevice device(&presentation);
|
||||
auto vertex = LoadShader(directory / "Triangle.vert.spv");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ add_library(libSceVideoOut SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/Buffer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Output.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/VideoOutDriver.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/DisplayWindow.cpp
|
||||
)
|
||||
|
||||
target_include_directories(libSceVideoOut PRIVATE
|
||||
|
|
@ -11,6 +12,9 @@ target_include_directories(libSceVideoOut PRIVATE
|
|||
${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include
|
||||
)
|
||||
target_link_libraries(libSceVideoOut PRIVATE libSceAgcDriver libc libkernel SDL2-static)
|
||||
if(WIN32)
|
||||
target_link_libraries(libSceVideoOut PRIVATE comctl32)
|
||||
endif()
|
||||
set_target_properties(libSceVideoOut PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(libSceVideoOut)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOOUT_INCLUDE_DISPLAYWINDOW_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEVIDEOOUT_INCLUDE_DISPLAYWINDOW_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "SDL.h"
|
||||
|
||||
inline constexpr std::uint32_t DisplayWindowMinimumWidth = 320;
|
||||
inline constexpr std::uint32_t DisplayWindowMinimumHeight = 180;
|
||||
|
||||
class DisplayWindow {
|
||||
public:
|
||||
DisplayWindow() = default;
|
||||
~DisplayWindow();
|
||||
DisplayWindow(const DisplayWindow&) = delete;
|
||||
DisplayWindow& operator=(const DisplayWindow&) = delete;
|
||||
|
||||
void Ensure(std::uint32_t sourceWidth, std::uint32_t sourceHeight);
|
||||
void Destroy() noexcept;
|
||||
SDL_Window* Handle() const;
|
||||
void DrawableSize(std::uint32_t& width, std::uint32_t& height) const;
|
||||
|
||||
private:
|
||||
void create(std::uint32_t sourceWidth, std::uint32_t sourceHeight);
|
||||
void updateAspectRatio(std::uint32_t sourceWidth, std::uint32_t sourceHeight);
|
||||
void installSubclass();
|
||||
void removeSubclass() noexcept;
|
||||
void applyAspectRatio(std::uintptr_t edge, void* rect) const;
|
||||
|
||||
static std::intptr_t __stdcall windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData);
|
||||
|
||||
SDL_Window* window = nullptr;
|
||||
std::uint32_t aspectWidth = 0;
|
||||
std::uint32_t aspectHeight = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <stdexcept>
|
||||
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
|
||||
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SceTypes.hpp"
|
||||
|
|
@ -182,7 +183,7 @@ private:
|
|||
std::array<std::shared_ptr<AgcDriver::IVideoOutput>, VIDEO_OUT_NUM_MAX> outputs;
|
||||
std::shared_ptr<FlipQueue> flipQueue = std::make_shared<FlipQueue>();
|
||||
|
||||
SDL_Window* window = nullptr;
|
||||
DisplayWindow window;
|
||||
|
||||
std::jthread presentThread;
|
||||
std::jthread vblankThread;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/AspectFit.hpp"
|
||||
#include "SDL_vulkan.h"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "SDL_syswm.h"
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void require(bool condition, const char* reason) {
|
||||
if (!condition) throw std::runtime_error(std::string("DisplayWindow: ") + reason);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr UINT_PTR DisplayWindowSubclassId = 0x41505335u;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
DisplayWindow::~DisplayWindow() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void DisplayWindow::Ensure(std::uint32_t sourceWidth, std::uint32_t sourceHeight) {
|
||||
require(sourceWidth != 0 && sourceHeight != 0, "source extent must be non-zero");
|
||||
if (window == nullptr) create(sourceWidth, sourceHeight);
|
||||
updateAspectRatio(sourceWidth, sourceHeight);
|
||||
}
|
||||
|
||||
void DisplayWindow::create(std::uint32_t sourceWidth, std::uint32_t sourceHeight) {
|
||||
SDL_Rect usable{};
|
||||
require(SDL_GetDisplayUsableBounds(0, &usable) == 0, SDL_GetError());
|
||||
std::uint32_t initialWidth = sourceWidth;
|
||||
std::uint32_t initialHeight = sourceHeight;
|
||||
if (usable.w > 0 && usable.h > 0 && (sourceWidth > static_cast<std::uint32_t>(usable.w) || sourceHeight > static_cast<std::uint32_t>(usable.h))) {
|
||||
const auto fitted = AgcDriver::ComputeContainSize_nid_postfix(sourceWidth, sourceHeight, static_cast<std::uint32_t>(usable.w), static_cast<std::uint32_t>(usable.h), false);
|
||||
initialWidth = fitted.width;
|
||||
initialHeight = fitted.height;
|
||||
}
|
||||
window = SDL_CreateWindow("PS5", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(initialWidth), static_cast<int>(initialHeight), SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
require(window != nullptr, SDL_GetError());
|
||||
SDL_SetWindowMinimumSize(window, static_cast<int>(DisplayWindowMinimumWidth), static_cast<int>(DisplayWindowMinimumHeight));
|
||||
installSubclass();
|
||||
}
|
||||
|
||||
void DisplayWindow::updateAspectRatio(std::uint32_t sourceWidth, std::uint32_t sourceHeight) {
|
||||
aspectWidth = sourceWidth;
|
||||
aspectHeight = sourceHeight;
|
||||
}
|
||||
|
||||
void DisplayWindow::Destroy() noexcept {
|
||||
if (window == nullptr) return;
|
||||
removeSubclass();
|
||||
SDL_DestroyWindow(window);
|
||||
window = nullptr;
|
||||
}
|
||||
|
||||
SDL_Window* DisplayWindow::Handle() const {
|
||||
return window;
|
||||
}
|
||||
|
||||
void DisplayWindow::DrawableSize(std::uint32_t& width, std::uint32_t& height) const {
|
||||
if (window == nullptr) {
|
||||
width = 0;
|
||||
height = 0;
|
||||
return;
|
||||
}
|
||||
int drawableWidth = 0;
|
||||
int drawableHeight = 0;
|
||||
SDL_Vulkan_GetDrawableSize(window, &drawableWidth, &drawableHeight);
|
||||
width = drawableWidth > 0 ? static_cast<std::uint32_t>(drawableWidth) : 0;
|
||||
height = drawableHeight > 0 ? static_cast<std::uint32_t>(drawableHeight) : 0;
|
||||
}
|
||||
|
||||
void DisplayWindow::installSubclass() {
|
||||
#ifdef _WIN32
|
||||
SDL_SysWMinfo info;
|
||||
SDL_VERSION(&info.version);
|
||||
require(SDL_GetWindowWMInfo(window, &info) == SDL_TRUE, SDL_GetError());
|
||||
require(info.subsystem == SDL_SYSWM_WINDOWS, "unsupported window subsystem");
|
||||
const auto attached = SetWindowSubclass(info.info.win.window, reinterpret_cast<SUBCLASSPROC>(&DisplayWindow::windowProc), DisplayWindowSubclassId, reinterpret_cast<DWORD_PTR>(this));
|
||||
require(attached != FALSE, "SetWindowSubclass failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayWindow::removeSubclass() noexcept {
|
||||
#ifdef _WIN32
|
||||
if (window == nullptr) return;
|
||||
SDL_SysWMinfo info;
|
||||
SDL_VERSION(&info.version);
|
||||
if (SDL_GetWindowWMInfo(window, &info) != SDL_TRUE) return;
|
||||
if (info.subsystem != SDL_SYSWM_WINDOWS) return;
|
||||
RemoveWindowSubclass(info.info.win.window, reinterpret_cast<SUBCLASSPROC>(&DisplayWindow::windowProc), DisplayWindowSubclassId);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayWindow::applyAspectRatio(std::uintptr_t edge, void* rect) const {
|
||||
#ifdef _WIN32
|
||||
if (aspectWidth == 0 || aspectHeight == 0) return;
|
||||
auto* bounds = static_cast<RECT*>(rect);
|
||||
const auto currentWidth = static_cast<std::uint32_t>(bounds->right - bounds->left);
|
||||
const auto currentHeight = static_cast<std::uint32_t>(bounds->bottom - bounds->top);
|
||||
if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) {
|
||||
const auto width = AgcDriver::ComputeWidthForHeight_nid_postfix(aspectWidth, aspectHeight, currentHeight);
|
||||
bounds->right = bounds->left + static_cast<LONG>(width);
|
||||
return;
|
||||
}
|
||||
const auto height = AgcDriver::ComputeHeightForWidth_nid_postfix(aspectWidth, aspectHeight, currentWidth);
|
||||
if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) bounds->top = bounds->bottom - static_cast<LONG>(height);
|
||||
else bounds->bottom = bounds->top + static_cast<LONG>(height);
|
||||
#else
|
||||
static_cast<void>(edge);
|
||||
static_cast<void>(rect);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::intptr_t __stdcall DisplayWindow::windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData) {
|
||||
#ifdef _WIN32
|
||||
static_cast<void>(subclassId);
|
||||
if (message == WM_SIZING) {
|
||||
reinterpret_cast<const DisplayWindow*>(referenceData)->applyAspectRatio(wParam, reinterpret_cast<void*>(lParam));
|
||||
}
|
||||
return DefSubclassProc(static_cast<HWND>(hwnd), message, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam));
|
||||
#else
|
||||
static_cast<void>(hwnd);
|
||||
static_cast<void>(message);
|
||||
static_cast<void>(wParam);
|
||||
static_cast<void>(lParam);
|
||||
static_cast<void>(subclassId);
|
||||
static_cast<void>(referenceData);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -182,9 +182,9 @@ void VideoOutDriver::Shutdown() {
|
|||
flipQueue->changed.notify_all();
|
||||
presentThread.join();
|
||||
vblankThread.join();
|
||||
if (window != nullptr) {
|
||||
AgcDriverReleaseWindow_nid_postfix(window);
|
||||
SDL_DestroyWindow(window);
|
||||
if (window.Handle() != nullptr) {
|
||||
AgcDriverReleaseWindow_nid_postfix(window.Handle());
|
||||
window.Destroy();
|
||||
}
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
stopped = true;
|
||||
|
|
@ -323,24 +323,22 @@ void VideoOutDriver::processFlip(FlipRequest& req) {
|
|||
checkConfig(*req.cfg);
|
||||
}
|
||||
require(req.width != 0 && req.height != 0 && req.width <= static_cast<uint32_t>(std::numeric_limits<int>::max()) && req.height <= static_cast<uint32_t>(std::numeric_limits<int>::max()), "invalid window dimensions");
|
||||
if (window == nullptr) {
|
||||
window = SDL_CreateWindow("PS5", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(req.width), static_cast<int>(req.height), SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN);
|
||||
if (window == nullptr) throw std::runtime_error(std::string("SDL_CreateWindow failed: ") + SDL_GetError());
|
||||
} else {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
SDL_GetWindowSize(window, &width, &height);
|
||||
if (width != static_cast<int>(req.width) || height != static_cast<int>(req.height)) SDL_SetWindowSize(window, static_cast<int>(req.width), static_cast<int>(req.height));
|
||||
}
|
||||
window.Ensure(req.width, req.height);
|
||||
unsigned extensionCount = 0;
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr)) throw std::runtime_error(std::string("SDL_Vulkan_GetInstanceExtensions failed: ") + SDL_GetError());
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(window.Handle(), &extensionCount, nullptr)) throw std::runtime_error(std::string("SDL_Vulkan_GetInstanceExtensions failed: ") + SDL_GetError());
|
||||
std::vector<const char*> extensions(extensionCount);
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensions.data())) throw std::runtime_error(std::string("SDL_Vulkan_GetInstanceExtensions failed: ") + SDL_GetError());
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(window.Handle(), &extensionCount, extensions.data())) throw std::runtime_error(std::string("SDL_Vulkan_GetInstanceExtensions failed: ") + SDL_GetError());
|
||||
extensions.resize(extensionCount);
|
||||
const AgcDriver::PresentationWindow target{window, extensions, [](void* context, VkInstance instance) {
|
||||
const AgcDriver::PresentationWindow target{window.Handle(), extensions, [](void* context, VkInstance instance) {
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
if (!SDL_Vulkan_CreateSurface(static_cast<SDL_Window*>(context), instance, &surface)) throw std::runtime_error(std::string("SDL_Vulkan_CreateSurface failed: ") + SDL_GetError());
|
||||
return surface;
|
||||
}, [](void* context, std::uint32_t* width, std::uint32_t* height) {
|
||||
int drawableWidth = 0;
|
||||
int drawableHeight = 0;
|
||||
SDL_Vulkan_GetDrawableSize(static_cast<SDL_Window*>(context), &drawableWidth, &drawableHeight);
|
||||
*width = drawableWidth > 0 ? static_cast<std::uint32_t>(drawableWidth) : 0;
|
||||
*height = drawableHeight > 0 ? static_cast<std::uint32_t>(drawableHeight) : 0;
|
||||
}, req.width, req.height};
|
||||
const auto gpuReady = [](void* context) {
|
||||
auto& request = *static_cast<FlipRequest*>(context);
|
||||
|
|
|
|||
Loading…
Reference in New Issue