code: Run clang-format
This commit is contained in:
@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/microprofile.h"
|
||||
#include "citra/config.h"
|
||||
#include "citra/emu_window/emu_window_sdl2.h"
|
||||
#include "citra/lodepng_image_interface.h"
|
||||
@ -17,6 +16,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
@ -98,11 +98,10 @@ void ConfigureCamera::ConnectEvents() {
|
||||
}
|
||||
});
|
||||
connect(ui->camera_file, &QLineEdit::textChanged, this, [this] { StopPreviewing(); });
|
||||
connect(ui->system_camera,
|
||||
qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->system_camera, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, [this] { StopPreviewing(); });
|
||||
}
|
||||
|
||||
void ConfigureCamera::UpdateCameraMode() {
|
||||
|
@ -92,7 +92,8 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
|
||||
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
|
||||
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
|
||||
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
|
||||
Settings::values.physical_device = static_cast<u16>(ui->physical_device_combo->currentIndex());
|
||||
}
|
||||
|
||||
|
@ -230,7 +230,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
continue;
|
||||
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
||||
Qt::CustomContextMenu);
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, this, [this, analog_id, sub_button_id]() {
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, this,
|
||||
[this, analog_id, sub_button_id]() {
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
[this, analog_id, sub_button_id](const Common::ParamPackage& params) {
|
||||
@ -242,7 +243,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id],
|
||||
&QPushButton::customContextMenuRequested, this, [this, analog_id, sub_button_id](const QPoint& menu_location) {
|
||||
&QPushButton::customContextMenuRequested, this,
|
||||
[this, analog_id, sub_button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
@ -280,8 +282,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
InputCommon::Polling::DeviceType::Analog);
|
||||
}
|
||||
});
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, this, [this, analog_id] {
|
||||
const int slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, this,
|
||||
[this, analog_id] {
|
||||
const int slider_value =
|
||||
analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
const auto engine = analogs_param[analog_id].Get("engine", "");
|
||||
if (engine == "sdl" || engine == "gcpad") {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
@ -356,8 +360,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile);
|
||||
connect(ui->buttonRename, &QPushButton::clicked, this, &ConfigureInput::RenameProfile);
|
||||
|
||||
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int i) {
|
||||
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int i) {
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||
Settings::LoadProfile(i);
|
||||
|
@ -316,56 +316,46 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
case Format::RGBA8: {
|
||||
auto value = Color::DecodeRGBA8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2),
|
||||
QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB8: {
|
||||
auto value = Color::DecodeRGB8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2),
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGB5A1: {
|
||||
auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2),
|
||||
QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB565: {
|
||||
auto value = Color::DecodeRGB565(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2),
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGBA4: {
|
||||
auto value = Color::DecodeRGBA4(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2),
|
||||
QString::number(value.a(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::IA8:
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
|
||||
case Format::RG8: {
|
||||
auto value = Color::DecodeRG8(pixel) / 255.0f;
|
||||
return QStringLiteral("Red: %1, Green: %2")
|
||||
.arg(QString::number(value.r(), 'f', 2),
|
||||
QString::number(value.g(), 'f', 2));
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2));
|
||||
}
|
||||
case Format::I8:
|
||||
return QStringLiteral("Index: %1").arg(*pixel);
|
||||
case Format::A8:
|
||||
return QStringLiteral("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
|
||||
case Format::IA4:
|
||||
return QStringLiteral("Index: %1, Alpha: %2")
|
||||
.arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
|
||||
case Format::I4: {
|
||||
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
||||
return QStringLiteral("Index: %1").arg(i);
|
||||
|
@ -407,7 +407,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
|
||||
static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
|
||||
input_data_mapper->setMapping(input_data[i], i);
|
||||
}
|
||||
connect(input_data_mapper, &QSignalMapper::mappedInt, this, &GraphicsVertexShaderWidget::OnInputAttributeChanged);
|
||||
connect(input_data_mapper, &QSignalMapper::mappedInt, this,
|
||||
&GraphicsVertexShaderWidget::OnInputAttributeChanged);
|
||||
|
||||
auto main_widget = new QWidget;
|
||||
auto main_layout = new QVBoxLayout;
|
||||
@ -514,7 +515,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
|
||||
int num_attributes = shader_config.max_input_attribute_index + 1;
|
||||
|
||||
for (auto pattern : shader_setup.swizzle_data) {
|
||||
const nihstro::SwizzleInfo swizzle_info = { .pattern = nihstro::SwizzlePattern{pattern} };
|
||||
const nihstro::SwizzleInfo swizzle_info = {.pattern = nihstro::SwizzlePattern{pattern}};
|
||||
info.swizzle_info.push_back(swizzle_info);
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,8 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void MicroProfileWidget::wheelEvent(QWheelEvent* event) {
|
||||
MicroProfileMousePosition(event->position().x() / x_scale, event->position().y() / y_scale, event->angleDelta().y() / 120);
|
||||
MicroProfileMousePosition(event->position().x() / x_scale, event->position().y() / y_scale,
|
||||
event->angleDelta().y() / 120);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
|
@ -169,8 +169,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
|
||||
* @return true if the haystack contains all words of userinput
|
||||
*/
|
||||
static bool ContainsAllWords(const QString& haystack, const QString& userinput) {
|
||||
const QStringList userinput_split =
|
||||
userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
|
||||
const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
|
||||
|
||||
return std::all_of(userinput_split.begin(), userinput_split.end(),
|
||||
[&haystack](const QString& s) { return haystack.contains(s); });
|
||||
|
@ -146,7 +146,9 @@ static void InitializeLogging() {
|
||||
#endif
|
||||
}
|
||||
|
||||
GMainWindow::GMainWindow() : ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{nullptr} {
|
||||
GMainWindow::GMainWindow()
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{
|
||||
nullptr} {
|
||||
InitializeLogging();
|
||||
Debugger::ToggleConsole();
|
||||
Settings::LogSettings();
|
||||
@ -2112,27 +2114,20 @@ void GMainWindow::ShowMouseCursor() {
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateAPIIndicator(bool override) {
|
||||
static std::array graphics_apis = {
|
||||
QStringLiteral("OPENGL"),
|
||||
QStringLiteral("OPENGLES"),
|
||||
QStringLiteral("VULKAN")
|
||||
};
|
||||
static std::array graphics_apis = {QStringLiteral("OPENGL"), QStringLiteral("OPENGLES"),
|
||||
QStringLiteral("VULKAN")};
|
||||
|
||||
static std::array graphics_api_colors = {
|
||||
QStringLiteral("#00ccdd"),
|
||||
QStringLiteral("#ba2a8d"),
|
||||
QStringLiteral("#91242a")
|
||||
};
|
||||
static std::array graphics_api_colors = {QStringLiteral("#00ccdd"), QStringLiteral("#ba2a8d"),
|
||||
QStringLiteral("#91242a")};
|
||||
|
||||
u32 api_index = static_cast<u32>(Settings::values.graphics_api);
|
||||
if (override) {
|
||||
api_index = (api_index + 1) % graphics_apis.size();
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(api_index);
|
||||
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(api_index);
|
||||
}
|
||||
|
||||
const QString style_sheet =
|
||||
QStringLiteral("QPushButton { font-weight: bold; color: %0; }").arg(graphics_api_colors[api_index]);
|
||||
const QString style_sheet = QStringLiteral("QPushButton { font-weight: bold; color: %0; }")
|
||||
.arg(graphics_api_colors[api_index]);
|
||||
|
||||
graphics_api_button->setText(graphics_apis[api_index]);
|
||||
graphics_api_button->setStyleSheet(style_sheet);
|
||||
|
@ -7,9 +7,9 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QMainWindow>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include <QPushButton>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <concepts>
|
||||
#include "common/cityhash.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <type_traits>
|
||||
#include <fmt/format.h>
|
||||
|
||||
// adapted from https://github.com/fmtlib/fmt/issues/2704
|
||||
// a generic formatter for enum classes
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <compare>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
@ -3,8 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
|
@ -63,7 +63,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(add)(unsigned int inst, int index) {
|
||||
|
||||
return inst_base;
|
||||
}
|
||||
static ARM_INST_PTR INTERPRETER_TRANSLATE(and)(unsigned int inst, int index) {
|
||||
static ARM_INST_PTR INTERPRETER_TRANSLATE (and)(unsigned int inst, int index) {
|
||||
arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(and_inst));
|
||||
and_inst* inst_cream = (and_inst*)inst_base->component;
|
||||
|
||||
|
@ -140,8 +140,8 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
|
||||
VAddr page_start = Common::AlignDown(source_address, Memory::CITRA_PAGE_SIZE);
|
||||
u32 page_offset = source_address - page_start;
|
||||
u32 num_pages =
|
||||
Common::AlignUp(page_offset + size, Memory::CITRA_PAGE_SIZE) >> Memory::CITRA_PAGE_BITS;
|
||||
u32 num_pages = Common::AlignUp(page_offset + size, Memory::CITRA_PAGE_SIZE) >>
|
||||
Memory::CITRA_PAGE_BITS;
|
||||
|
||||
// Skip when the size is zero and num_pages == 0
|
||||
if (size == 0) {
|
||||
@ -180,8 +180,9 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
next_vma.meminfo_state == MemoryState::Reserved);
|
||||
|
||||
// Unmap the buffer and guard pages from the source process
|
||||
ResultCode result = src_process->vm_manager.UnmapRange(
|
||||
page_start - Memory::CITRA_PAGE_SIZE, (num_pages + 2) * Memory::CITRA_PAGE_SIZE);
|
||||
ResultCode result =
|
||||
src_process->vm_manager.UnmapRange(page_start - Memory::CITRA_PAGE_SIZE,
|
||||
(num_pages + 2) * Memory::CITRA_PAGE_SIZE);
|
||||
ASSERT(result == RESULT_SUCCESS);
|
||||
|
||||
mapped_buffer_context.erase(found);
|
||||
|
@ -26,47 +26,66 @@ struct AppletTitleData {
|
||||
|
||||
static constexpr std::size_t NumApplets = 29;
|
||||
static constexpr std::array<AppletTitleData, NumApplets> applet_titleids = {{
|
||||
{{AppletId::HomeMenu, AppletId::None}, {0x4003000008202, 0x4003000008F02, 0x4003000009802,
|
||||
0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102}},
|
||||
{{AppletId::AlternateMenu, AppletId::None}, {0x4003000008102, 0x4003000008102, 0x4003000008102,
|
||||
0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102}},
|
||||
{{AppletId::Camera, AppletId::None}, {0x4003000008402, 0x4003000009002, 0x4003000009902,
|
||||
0x4003000008402, 0x400300000A202, 0x400300000AA02, 0x400300000B202}},
|
||||
{{AppletId::FriendList, AppletId::None}, {0x4003000008D02, 0x4003000009602, 0x4003000009F02,
|
||||
0x4003000008D02, 0x400300000A702, 0x400300000AF02, 0x400300000B702}},
|
||||
{{AppletId::GameNotes, AppletId::None}, {0x4003000008702, 0x4003000009302, 0x4003000009C02,
|
||||
0x4003000008702, 0x400300000A502, 0x400300000AD02, 0x400300000B502}},
|
||||
{{AppletId::InternetBrowser, AppletId::None}, {0x4003000008802, 0x4003000009402, 0x4003000009D02,
|
||||
0x4003000008802, 0x400300000A602, 0x400300000AE02, 0x400300000B602}},
|
||||
{{AppletId::InstructionManual, AppletId::None}, {0x4003000008602, 0x4003000009202, 0x4003000009B02,
|
||||
0x4003000008602, 0x400300000A402, 0x400300000AC02, 0x400300000B402}},
|
||||
{{AppletId::Notifications, AppletId::None}, {0x4003000008E02, 0x4003000009702, 0x400300000A002,
|
||||
0x4003000008E02, 0x400300000A802, 0x400300000B002, 0x400300000B802}},
|
||||
{{AppletId::Miiverse, AppletId::None}, {0x400300000BC02, 0x400300000BD02, 0x400300000BE02,
|
||||
0x400300000BC02, 0x4003000009E02, 0x4003000009502, 0x400300000B902}},
|
||||
{{AppletId::HomeMenu, AppletId::None},
|
||||
{0x4003000008202, 0x4003000008F02, 0x4003000009802, 0x4003000008202, 0x400300000A102,
|
||||
0x400300000A902, 0x400300000B102}},
|
||||
{{AppletId::AlternateMenu, AppletId::None},
|
||||
{0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102,
|
||||
0x4003000008102, 0x4003000008102}},
|
||||
{{AppletId::Camera, AppletId::None},
|
||||
{0x4003000008402, 0x4003000009002, 0x4003000009902, 0x4003000008402, 0x400300000A202,
|
||||
0x400300000AA02, 0x400300000B202}},
|
||||
{{AppletId::FriendList, AppletId::None},
|
||||
{0x4003000008D02, 0x4003000009602, 0x4003000009F02, 0x4003000008D02, 0x400300000A702,
|
||||
0x400300000AF02, 0x400300000B702}},
|
||||
{{AppletId::GameNotes, AppletId::None},
|
||||
{0x4003000008702, 0x4003000009302, 0x4003000009C02, 0x4003000008702, 0x400300000A502,
|
||||
0x400300000AD02, 0x400300000B502}},
|
||||
{{AppletId::InternetBrowser, AppletId::None},
|
||||
{0x4003000008802, 0x4003000009402, 0x4003000009D02, 0x4003000008802, 0x400300000A602,
|
||||
0x400300000AE02, 0x400300000B602}},
|
||||
{{AppletId::InstructionManual, AppletId::None},
|
||||
{0x4003000008602, 0x4003000009202, 0x4003000009B02, 0x4003000008602, 0x400300000A402,
|
||||
0x400300000AC02, 0x400300000B402}},
|
||||
{{AppletId::Notifications, AppletId::None},
|
||||
{0x4003000008E02, 0x4003000009702, 0x400300000A002, 0x4003000008E02, 0x400300000A802,
|
||||
0x400300000B002, 0x400300000B802}},
|
||||
{{AppletId::Miiverse, AppletId::None},
|
||||
{0x400300000BC02, 0x400300000BD02, 0x400300000BE02, 0x400300000BC02, 0x4003000009E02,
|
||||
0x4003000009502, 0x400300000B902}},
|
||||
// These values obtained from an older NS dump firmware 4.5
|
||||
{{AppletId::MiiversePost, AppletId::None}, {0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
||||
0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02}},
|
||||
{{AppletId::MiiversePost, AppletId::None},
|
||||
{0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
||||
0x400300000BA02, 0x400300000BA02}},
|
||||
// {AppletId::MiiversePost, AppletId::None, 0x4003000008302, 0x4003000008B02, 0x400300000BA02,
|
||||
// 0x4003000008302, 0x0, 0x0, 0x0},
|
||||
{{AppletId::AmiiboSettings, AppletId::None}, {0x4003000009502, 0x4003000009E02, 0x400300000B902,
|
||||
0x4003000009502, 0x0, 0x4003000008C02, 0x400300000BF02}},
|
||||
{{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2}, {0x400300000C002, 0x400300000C802,
|
||||
0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402}},
|
||||
{{AppletId::Ed1, AppletId::Ed2}, {0x400300000C102, 0x400300000C902, 0x400300000D102,
|
||||
0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502}},
|
||||
{{AppletId::PnoteApp, AppletId::PnoteApp2}, {0x400300000C302, 0x400300000CB02, 0x400300000D302,
|
||||
0x400300000C302, 0x400300000DB02, 0x400300000E102, 0x400300000E702}},
|
||||
{{AppletId::SnoteApp, AppletId::SnoteApp2}, {0x400300000C402, 0x400300000CC02, 0x400300000D402,
|
||||
0x400300000C402, 0x400300000DC02, 0x400300000E202, 0x400300000E802}},
|
||||
{{AppletId::Error, AppletId::Error2}, {0x400300000C502, 0x400300000C502, 0x400300000C502,
|
||||
0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02}},
|
||||
{{AppletId::Mint, AppletId::Mint2}, {0x400300000C602, 0x400300000CE02, 0x400300000D602,
|
||||
0x400300000C602, 0x400300000DD02, 0x400300000E302, 0x400300000E902}},
|
||||
{{AppletId::Extrapad, AppletId::Extrapad2}, {0x400300000CD02, 0x400300000CD02, 0x400300000CD02,
|
||||
0x400300000CD02, 0x400300000D502, 0x400300000D502, 0x400300000D502}},
|
||||
{{AppletId::Memolib, AppletId::Memolib2}, {0x400300000F602, 0x400300000F602, 0x400300000F602,
|
||||
0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602}},
|
||||
{{AppletId::AmiiboSettings, AppletId::None},
|
||||
{0x4003000009502, 0x4003000009E02, 0x400300000B902, 0x4003000009502, 0x0, 0x4003000008C02,
|
||||
0x400300000BF02}},
|
||||
{{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2},
|
||||
{0x400300000C002, 0x400300000C802, 0x400300000D002, 0x400300000C002, 0x400300000D802,
|
||||
0x400300000DE02, 0x400300000E402}},
|
||||
{{AppletId::Ed1, AppletId::Ed2},
|
||||
{0x400300000C102, 0x400300000C902, 0x400300000D102, 0x400300000C102, 0x400300000D902,
|
||||
0x400300000DF02, 0x400300000E502}},
|
||||
{{AppletId::PnoteApp, AppletId::PnoteApp2},
|
||||
{0x400300000C302, 0x400300000CB02, 0x400300000D302, 0x400300000C302, 0x400300000DB02,
|
||||
0x400300000E102, 0x400300000E702}},
|
||||
{{AppletId::SnoteApp, AppletId::SnoteApp2},
|
||||
{0x400300000C402, 0x400300000CC02, 0x400300000D402, 0x400300000C402, 0x400300000DC02,
|
||||
0x400300000E202, 0x400300000E802}},
|
||||
{{AppletId::Error, AppletId::Error2},
|
||||
{0x400300000C502, 0x400300000C502, 0x400300000C502, 0x400300000C502, 0x400300000CF02,
|
||||
0x400300000CF02, 0x400300000CF02}},
|
||||
{{AppletId::Mint, AppletId::Mint2},
|
||||
{0x400300000C602, 0x400300000CE02, 0x400300000D602, 0x400300000C602, 0x400300000DD02,
|
||||
0x400300000E302, 0x400300000E902}},
|
||||
{{AppletId::Extrapad, AppletId::Extrapad2},
|
||||
{0x400300000CD02, 0x400300000CD02, 0x400300000CD02, 0x400300000CD02, 0x400300000D502,
|
||||
0x400300000D502, 0x400300000D502}},
|
||||
{{AppletId::Memolib, AppletId::Memolib2},
|
||||
{0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602,
|
||||
0x400300000F602, 0x400300000F602}},
|
||||
// TODO(Subv): Fill in the rest of the titleids
|
||||
}};
|
||||
|
||||
|
@ -111,7 +111,8 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->RODataSegment().offset + codeset->RODataSegment().size;
|
||||
codeset->DataSegment().addr = overlay_ncch->exheader_header.codeset_info.data.address;
|
||||
codeset->DataSegment().size =
|
||||
overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::CITRA_PAGE_SIZE +
|
||||
overlay_ncch->exheader_header.codeset_info.data.num_max_pages *
|
||||
Memory::CITRA_PAGE_SIZE +
|
||||
bss_page_size;
|
||||
|
||||
// Apply patches now that the entire codeset (including .bss) has been allocated
|
||||
|
@ -221,8 +221,8 @@ std::shared_ptr<PageTable> MemorySystem::GetCurrentPageTable() const {
|
||||
|
||||
void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory,
|
||||
PageType type) {
|
||||
LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory.GetPtr(), base * CITRA_PAGE_SIZE,
|
||||
(base + size) * CITRA_PAGE_SIZE);
|
||||
LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory.GetPtr(),
|
||||
base * CITRA_PAGE_SIZE, (base + size) * CITRA_PAGE_SIZE);
|
||||
|
||||
RasterizerFlushVirtualRegion(base << CITRA_PAGE_BITS, size * CITRA_PAGE_SIZE,
|
||||
FlushMode::FlushAndInvalidate);
|
||||
@ -256,7 +256,8 @@ void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size,
|
||||
MMIORegionPointer mmio_handler) {
|
||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr, PageType::Special);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
||||
PageType::Special);
|
||||
|
||||
page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
||||
}
|
||||
@ -264,7 +265,8 @@ void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size,
|
||||
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
||||
PageType::Unmapped);
|
||||
}
|
||||
|
||||
MemoryRef MemorySystem::GetPointerForRasterizerCache(VAddr addr) const {
|
||||
@ -494,8 +496,7 @@ MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const {
|
||||
std::make_pair(N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE),
|
||||
};
|
||||
|
||||
const auto area =
|
||||
std::ranges::find_if(memory_areas, [&](const auto& area) {
|
||||
const auto area = std::ranges::find_if(memory_areas, [&](const auto& area) {
|
||||
// Note: the region end check is inclusive because the user can pass in an address that
|
||||
// represents an open right bound
|
||||
return address >= area.first && address <= area.first + area.second;
|
||||
|
@ -14,11 +14,7 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
enum class GraphicsAPI {
|
||||
OpenGL = 0,
|
||||
OpenGLES = 1,
|
||||
Vulkan = 2
|
||||
};
|
||||
enum class GraphicsAPI { OpenGL = 0, OpenGLES = 1, Vulkan = 2 };
|
||||
|
||||
enum class InitClock {
|
||||
SystemTime = 0,
|
||||
|
@ -5,9 +5,9 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <numbers>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <numbers>
|
||||
#include "common/math_util.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "common/thread.h"
|
||||
|
@ -8,13 +8,13 @@
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <numbers>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <numbers>
|
||||
#include <SDL.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/param_package.h"
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#include <limits>
|
||||
#include "core/memory.h"
|
||||
#include "video_core/video_core.h"
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
@ -53,21 +53,18 @@ void RasterizerAccelerated::UpdatePagesCachedCount(PAddr addr, u32 size, int del
|
||||
|
||||
cache_bytes += Memory::CITRA_PAGE_SIZE;
|
||||
} else if (cache_bytes > 0) {
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(cache_start_addr, cache_bytes,
|
||||
true);
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(cache_start_addr, cache_bytes, true);
|
||||
|
||||
cache_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (uncache_bytes > 0) {
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(uncache_start_addr, uncache_bytes,
|
||||
false);
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(uncache_start_addr, uncache_bytes, false);
|
||||
}
|
||||
|
||||
if (cache_bytes > 0) {
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(cache_start_addr, cache_bytes,
|
||||
true);
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(cache_start_addr, cache_bytes, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +95,7 @@ void RasterizerAccelerated::ClearAll(bool flush) {
|
||||
}
|
||||
|
||||
if (uncache_bytes > 0) {
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(uncache_start_addr, uncache_bytes,
|
||||
false);
|
||||
VideoCore::g_memory->RasterizerMarkRegionCached(uncache_start_addr, uncache_bytes, false);
|
||||
}
|
||||
|
||||
cached_pages = {};
|
||||
|
@ -3,9 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <span>
|
||||
#include <bit>
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <span>
|
||||
#include "common/alignment.h"
|
||||
#include "common/color.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
@ -108,7 +108,8 @@ inline void EncodePixel(const std::byte* source, std::byte* dest) {
|
||||
}
|
||||
|
||||
template <bool morton_to_linear, PixelFormat format>
|
||||
inline void MortonCopyTile(u32 stride, std::span<std::byte> tile_buffer, std::span<std::byte> linear_buffer) {
|
||||
inline void MortonCopyTile(u32 stride, std::span<std::byte> tile_buffer,
|
||||
std::span<std::byte> linear_buffer) {
|
||||
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||
constexpr u32 linear_bytes_per_pixel = GetBytesPerPixel(format);
|
||||
constexpr bool is_compressed = format == PixelFormat::ETC1 || format == PixelFormat::ETC1A4;
|
||||
@ -116,10 +117,10 @@ inline void MortonCopyTile(u32 stride, std::span<std::byte> tile_buffer, std::sp
|
||||
|
||||
for (u32 y = 0; y < 8; y++) {
|
||||
for (u32 x = 0; x < 8; x++) {
|
||||
const auto tiled_pixel = tile_buffer.subspan(VideoCore::MortonInterleave(x, y) * bytes_per_pixel,
|
||||
bytes_per_pixel);
|
||||
const auto linear_pixel = linear_buffer.subspan(((7 - y) * stride + x) * linear_bytes_per_pixel,
|
||||
linear_bytes_per_pixel);
|
||||
const auto tiled_pixel = tile_buffer.subspan(
|
||||
VideoCore::MortonInterleave(x, y) * bytes_per_pixel, bytes_per_pixel);
|
||||
const auto linear_pixel = linear_buffer.subspan(
|
||||
((7 - y) * stride + x) * linear_bytes_per_pixel, linear_bytes_per_pixel);
|
||||
if constexpr (morton_to_linear) {
|
||||
if constexpr (is_compressed) {
|
||||
DecodePixelETC1<format>(x, y, tile_buffer.data(), linear_pixel.data());
|
||||
@ -138,24 +139,26 @@ inline void MortonCopyTile(u32 stride, std::span<std::byte> tile_buffer, std::sp
|
||||
/**
|
||||
* @brief Performs morton to/from linear convertions on the provided pixel data
|
||||
* @param width, height The dimentions of the rectangular region of pixels in linear_buffer
|
||||
* @param start_offset The number of bytes from the start of the first tile to the start of tiled_buffer
|
||||
* @param start_offset The number of bytes from the start of the first tile to the start of
|
||||
* tiled_buffer
|
||||
* @param end_offset The number of bytes from the start of the first tile to the end of tiled_buffer
|
||||
* @param linear_buffer The linear pixel data
|
||||
* @param tiled_buffer The tiled pixel data
|
||||
*
|
||||
* The MortonCopy is at the heart of the PICA texture implementation, as it's responsible for converting between
|
||||
* linear and morton tiled layouts. The function handles both convertions but there are slightly different
|
||||
* paths and inputs for each:
|
||||
* The MortonCopy is at the heart of the PICA texture implementation, as it's responsible for
|
||||
* converting between linear and morton tiled layouts. The function handles both convertions but
|
||||
* there are slightly different paths and inputs for each:
|
||||
*
|
||||
* Morton to Linear:
|
||||
* During uploads, tiled_buffer is always aligned to the tile or scanline boundary depending if the linear rectangle
|
||||
* spans multiple vertical tiles. linear_buffer does not reference the entire texture area, but rather the
|
||||
* specific rectangle affected by the upload.
|
||||
* During uploads, tiled_buffer is always aligned to the tile or scanline boundary depending if the
|
||||
* linear rectangle spans multiple vertical tiles. linear_buffer does not reference the entire
|
||||
* texture area, but rather the specific rectangle affected by the upload.
|
||||
*
|
||||
* Linear to Morton:
|
||||
* This is similar to the other convertion but with some differences. In this case tiled_buffer is not required
|
||||
* to be aligned to any specific boundary which requires special care. start_offset/end_offset are useful
|
||||
* here as they tell us exactly where the data should be placed in the linear_buffer.
|
||||
* This is similar to the other convertion but with some differences. In this case tiled_buffer is
|
||||
* not required to be aligned to any specific boundary which requires special care.
|
||||
* start_offset/end_offset are useful here as they tell us exactly where the data should be placed
|
||||
* in the linear_buffer.
|
||||
*/
|
||||
template <bool morton_to_linear, PixelFormat format>
|
||||
static void MortonCopy(u32 width, u32 height, u32 start_offset, u32 end_offset,
|
||||
@ -170,7 +173,8 @@ static void MortonCopy(u32 width, u32 height, u32 start_offset, u32 end_offset,
|
||||
const u32 aligned_start_offset = Common::AlignUp(start_offset, tile_size);
|
||||
const u32 aligned_end_offset = Common::AlignDown(end_offset, tile_size);
|
||||
|
||||
ASSERT(!morton_to_linear || (aligned_start_offset == start_offset && aligned_end_offset == end_offset));
|
||||
ASSERT(!morton_to_linear ||
|
||||
(aligned_start_offset == start_offset && aligned_end_offset == end_offset));
|
||||
|
||||
// In OpenGL the texture origin is in the bottom left corner as opposed to other
|
||||
// APIs that have it at the top left. To avoid flipping texture coordinates in
|
||||
@ -222,7 +226,8 @@ static void MortonCopy(u32 width, u32 height, u32 start_offset, u32 end_offset,
|
||||
std::array<std::byte, tile_size> tmp_buf;
|
||||
auto linear_data = linear_buffer.subspan(linear_offset, linear_tile_stride);
|
||||
MortonCopyTile<morton_to_linear, format>(width, tmp_buf, linear_data);
|
||||
std::memcpy(tiled_buffer.data() + tiled_offset, tmp_buf.data(), end_offset - aligned_end_offset);
|
||||
std::memcpy(tiled_buffer.data() + tiled_offset, tmp_buf.data(),
|
||||
end_offset - aligned_end_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,13 @@
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
MICROPROFILE_DEFINE(RasterizerCache_BlitSurface, "RasterizerCache", "BlitSurface", MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_CopySurface, "RasterizerCache", "CopySurface", MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load", MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceFlush, "RasterizerCache", "Surface Flush", MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_BlitSurface, "RasterizerCache", "BlitSurface",
|
||||
MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_CopySurface, "RasterizerCache", "CopySurface",
|
||||
MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load",
|
||||
MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(RasterizerCache_SurfaceFlush, "RasterizerCache", "Surface Flush",
|
||||
MP_RGB(128, 192, 64));
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include "common/alignment.h"
|
||||
@ -14,8 +14,8 @@
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
#include "video_core/texture/texture_decode.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
@ -54,15 +54,17 @@ public:
|
||||
private:
|
||||
/// Declare rasterizer interval types
|
||||
using SurfaceSet = std::set<Surface>;
|
||||
using SurfaceMap =
|
||||
boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less,
|
||||
boost::icl::inplace_plus, boost::icl::inter_section, SurfaceInterval>;
|
||||
using SurfaceCache =
|
||||
boost::icl::interval_map<PAddr, SurfaceSet, boost::icl::partial_absorber, std::less,
|
||||
boost::icl::inplace_plus, boost::icl::inter_section, SurfaceInterval>;
|
||||
using SurfaceMap = boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber,
|
||||
std::less, boost::icl::inplace_plus,
|
||||
boost::icl::inter_section, SurfaceInterval>;
|
||||
using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet, boost::icl::partial_absorber,
|
||||
std::less, boost::icl::inplace_plus,
|
||||
boost::icl::inter_section, SurfaceInterval>;
|
||||
|
||||
static_assert(std::is_same<SurfaceRegions::interval_type, typename SurfaceCache::interval_type>() &&
|
||||
std::is_same<typename SurfaceMap::interval_type, typename SurfaceCache::interval_type>(),
|
||||
static_assert(
|
||||
std::is_same<SurfaceRegions::interval_type, typename SurfaceCache::interval_type>() &&
|
||||
std::is_same<typename SurfaceMap::interval_type,
|
||||
typename SurfaceCache::interval_type>(),
|
||||
"Incorrect interval types");
|
||||
|
||||
using SurfaceRect_Tuple = std::tuple<Surface, Common::Rectangle<u32>>;
|
||||
@ -170,7 +172,8 @@ private:
|
||||
};
|
||||
|
||||
template <class T>
|
||||
RasterizerCache<T>::RasterizerCache(VideoCore::RasterizerAccelerated& rasterizer, TextureRuntime& runtime)
|
||||
RasterizerCache<T>::RasterizerCache(VideoCore::RasterizerAccelerated& rasterizer,
|
||||
TextureRuntime& runtime)
|
||||
: rasterizer{rasterizer}, runtime{runtime} {
|
||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||
}
|
||||
@ -271,8 +274,7 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
|
||||
const Surface& dst_surface, Common::Rectangle<u32> dst_rect) {
|
||||
MICROPROFILE_SCOPE(RasterizerCache_BlitSurface);
|
||||
|
||||
if (!CheckFormatsBlittable(src_surface->pixel_format,
|
||||
dst_surface->pixel_format)) [[unlikely]] {
|
||||
if (!CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -282,28 +284,23 @@ bool RasterizerCache<T>::BlitSurfaces(const Surface& src_surface, Common::Rectan
|
||||
// 1. No scaling (the dimentions of src and dest rect are the same)
|
||||
// 2. No flipping (if the bottom value is bigger than the top this indicates texture flip)
|
||||
if (src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight() &&
|
||||
src_rect.bottom < src_rect.top) {
|
||||
const TextureCopy texture_copy = {
|
||||
.src_level = 0,
|
||||
src_rect.GetHeight() == dst_rect.GetHeight() && src_rect.bottom < src_rect.top) {
|
||||
const TextureCopy texture_copy = {.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
.dst_layer = 0,
|
||||
.src_offset = {src_rect.left, src_rect.bottom},
|
||||
.dst_offset = {dst_rect.left, dst_rect.bottom},
|
||||
.extent = {src_rect.GetWidth(), src_rect.GetHeight()}
|
||||
};
|
||||
.extent = {src_rect.GetWidth(), src_rect.GetHeight()}};
|
||||
|
||||
return runtime.CopyTextures(*src_surface, *dst_surface, texture_copy);
|
||||
} else {
|
||||
const TextureBlit texture_blit = {
|
||||
.src_level = 0,
|
||||
const TextureBlit texture_blit = {.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
.dst_layer = 0,
|
||||
.src_rect = src_rect,
|
||||
.dst_rect = dst_rect
|
||||
};
|
||||
.dst_rect = dst_rect};
|
||||
|
||||
return runtime.BlitTextures(*src_surface, *dst_surface, texture_blit);
|
||||
}
|
||||
@ -332,23 +329,20 @@ void RasterizerCache<T>::CopySurface(const Surface& src_surface, const Surface&
|
||||
const ClearValue clear_value =
|
||||
MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data());
|
||||
const TextureClear clear_rect = {
|
||||
.texture_level = 0,
|
||||
.texture_rect = dst_surface->GetScaledSubRect(subrect_params)
|
||||
};
|
||||
.texture_level = 0, .texture_rect = dst_surface->GetScaledSubRect(subrect_params)};
|
||||
|
||||
runtime.ClearTexture(*dst_surface, clear_rect, clear_value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_surface->CanSubRect(subrect_params)) {
|
||||
const TextureBlit texture_blit = {
|
||||
.src_level = 0,
|
||||
const TextureBlit texture_blit = {.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
.dst_layer = 0,
|
||||
.src_rect = src_surface->GetScaledSubRect(subrect_params),
|
||||
.dst_rect = dst_surface->GetScaledSubRect(subrect_params)
|
||||
};
|
||||
.dst_rect =
|
||||
dst_surface->GetScaledSubRect(subrect_params)};
|
||||
|
||||
runtime.BlitTextures(*src_surface, *dst_surface, texture_blit);
|
||||
return;
|
||||
@ -488,13 +482,15 @@ auto RasterizerCache<T>::GetSurfaceSubRect(const SurfaceParams& params, ScaleMat
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto RasterizerCache<T>::GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config) -> Surface {
|
||||
auto RasterizerCache<T>::GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config)
|
||||
-> Surface {
|
||||
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format);
|
||||
return GetTextureSurface(info, config.config.lod.max_level);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level) -> Surface {
|
||||
auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level)
|
||||
-> Surface {
|
||||
if (info.physical_address == 0) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
@ -572,15 +568,13 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
|
||||
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
|
||||
}
|
||||
|
||||
if (/*texture_filterer->IsNull()*/true) {
|
||||
const TextureBlit texture_blit = {
|
||||
.src_level = 0,
|
||||
if (/*texture_filterer->IsNull()*/ true) {
|
||||
const TextureBlit texture_blit = {.src_level = 0,
|
||||
.dst_level = level,
|
||||
.src_layer = 0,
|
||||
.dst_layer = 0,
|
||||
.src_rect = level_surface->GetScaledRect(),
|
||||
.dst_rect = surface_params.GetScaledRect()
|
||||
};
|
||||
.dst_rect = surface_params.GetScaledRect()};
|
||||
|
||||
runtime.BlitTextures(*level_surface, *surface, texture_blit);
|
||||
}
|
||||
@ -597,15 +591,13 @@ template <class T>
|
||||
auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> const Surface& {
|
||||
auto [it, new_surface] = texture_cube_cache.try_emplace(config);
|
||||
if (new_surface) {
|
||||
SurfaceParams cube_params = {
|
||||
.addr = config.px,
|
||||
SurfaceParams cube_params = {.addr = config.px,
|
||||
.width = config.width,
|
||||
.height = config.width,
|
||||
.stride = config.width,
|
||||
.texture_type = TextureType::CubeMap,
|
||||
.pixel_format = PixelFormatFromTextureFormat(config.format),
|
||||
.type = SurfaceType::Texture
|
||||
};
|
||||
.type = SurfaceType::Texture};
|
||||
|
||||
it->second = CreateSurface(cube_params);
|
||||
}
|
||||
@ -614,8 +606,7 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
|
||||
|
||||
// Update surface watchers
|
||||
auto& watchers = cube->level_watchers;
|
||||
const std::array addresses = {config.px, config.nx, config.py,
|
||||
config.ny, config.pz, config.nz};
|
||||
const std::array addresses = {config.px, config.nx, config.py, config.ny, config.pz, config.nz};
|
||||
|
||||
for (std::size_t i = 0; i < addresses.size(); i++) {
|
||||
auto& watcher = watchers[i];
|
||||
@ -650,14 +641,12 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
|
||||
ValidateSurface(face, face->addr, face->size);
|
||||
}
|
||||
|
||||
const TextureBlit texture_blit = {
|
||||
.src_level = 0,
|
||||
const TextureBlit texture_blit = {.src_level = 0,
|
||||
.dst_level = 0,
|
||||
.src_layer = 0,
|
||||
.dst_layer = static_cast<u32>(i),
|
||||
.src_rect = face->GetScaledRect(),
|
||||
.dst_rect = Rect2D{0, scaled_size, scaled_size, 0}
|
||||
};
|
||||
.dst_rect = Rect2D{0, scaled_size, scaled_size, 0}};
|
||||
|
||||
runtime.BlitTextures(*face, *cube, texture_blit);
|
||||
watcher->Validate();
|
||||
@ -680,7 +669,8 @@ auto RasterizerCache<T>::GetFramebufferSurfaces(bool using_color_fb, bool using_
|
||||
const bool texture_filter_changed =
|
||||
/*VideoCore::g_texture_filter_update_requested.exchange(false) &&
|
||||
texture_filterer->Reset(Settings::values.texture_filter_name,
|
||||
VideoCore::GetResolutionScaleFactor())*/false;
|
||||
VideoCore::GetResolutionScaleFactor())*/
|
||||
false;
|
||||
|
||||
if (resolution_scale_changed || texture_filter_changed) [[unlikely]] {
|
||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||
@ -926,19 +916,18 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
|
||||
|
||||
const auto upload_data = source_ptr.GetWriteBytes(load_info.end - load_info.addr);
|
||||
if (surface->is_tiled) {
|
||||
std::vector<std::byte> unswizzled_data(load_info.width * load_info.height * GetBytesPerPixel(load_info.pixel_format));
|
||||
std::vector<std::byte> unswizzled_data(load_info.width * load_info.height *
|
||||
GetBytesPerPixel(load_info.pixel_format));
|
||||
UnswizzleTexture(load_info, load_info.addr, load_info.end, upload_data, unswizzled_data);
|
||||
runtime.FormatConvert(*surface, true, unswizzled_data, staging.mapped);
|
||||
} else {
|
||||
runtime.FormatConvert(*surface, true, upload_data, staging.mapped);
|
||||
}
|
||||
|
||||
const BufferTextureCopy upload = {
|
||||
.buffer_offset = 0,
|
||||
const BufferTextureCopy upload = {.buffer_offset = 0,
|
||||
.buffer_size = staging.size,
|
||||
.texture_rect = surface->GetSubRect(load_info),
|
||||
.texture_level = 0
|
||||
};
|
||||
.texture_level = 0};
|
||||
|
||||
surface->Upload(upload, staging);
|
||||
}
|
||||
@ -953,16 +942,15 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
|
||||
|
||||
const auto& staging = runtime.FindStaging(
|
||||
flush_info.width * flush_info.height * surface->GetInternalBytesPerPixel(), false);
|
||||
const BufferTextureCopy download = {
|
||||
.buffer_offset = 0,
|
||||
const BufferTextureCopy download = {.buffer_offset = 0,
|
||||
.buffer_size = staging.size,
|
||||
.texture_rect = surface->GetSubRect(flush_info),
|
||||
.texture_level = 0
|
||||
};
|
||||
.texture_level = 0};
|
||||
|
||||
surface->Download(download, staging);
|
||||
|
||||
download_queue.push_back([this, surface, flush_start, flush_end, flush_info, mapped = staging.mapped]() {
|
||||
download_queue.push_back(
|
||||
[this, surface, flush_start, flush_end, flush_info, mapped = staging.mapped]() {
|
||||
MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start);
|
||||
if (!dest_ptr) [[unlikely]] {
|
||||
return;
|
||||
@ -971,7 +959,8 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
|
||||
const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start);
|
||||
|
||||
if (surface->is_tiled) {
|
||||
std::vector<std::byte> temp_data(flush_info.width * flush_info.height * GetBytesPerPixel(flush_info.pixel_format));
|
||||
std::vector<std::byte> temp_data(flush_info.width * flush_info.height *
|
||||
GetBytesPerPixel(flush_info.pixel_format));
|
||||
runtime.FormatConvert(*surface, false, mapped, temp_data);
|
||||
SwizzleTexture(flush_info, flush_start, flush_end, temp_data, download_dest);
|
||||
} else {
|
||||
@ -992,7 +981,8 @@ void RasterizerCache<T>::DownloadFillSurface(const Surface& surface, SurfaceInte
|
||||
}
|
||||
|
||||
const u32 start_offset = flush_start - surface->addr;
|
||||
const u32 download_size = std::clamp(flush_end - flush_start, 0u, static_cast<u32>(dest_ptr.GetSize()));
|
||||
const u32 download_size =
|
||||
std::clamp(flush_end - flush_start, 0u, static_cast<u32>(dest_ptr.GetSize()));
|
||||
const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size);
|
||||
const u32 backup_bytes = start_offset % surface->fill_size;
|
||||
|
||||
@ -1012,7 +1002,8 @@ void RasterizerCache<T>::DownloadFillSurface(const Surface& surface, SurfaceInte
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params,
|
||||
bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface,
|
||||
SurfaceParams& params,
|
||||
SurfaceInterval interval) {
|
||||
static constexpr std::array all_formats = {
|
||||
PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565,
|
||||
@ -1043,13 +1034,13 @@ bool RasterizerCache<T>::NoUnimplementedReinterpretations(const Surface& surface
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool RasterizerCache<T>::IntervalHasInvalidPixelFormat(SurfaceParams& params, SurfaceInterval interval) {
|
||||
bool RasterizerCache<T>::IntervalHasInvalidPixelFormat(SurfaceParams& params,
|
||||
SurfaceInterval interval) {
|
||||
params.pixel_format = PixelFormat::Invalid;
|
||||
for (const auto& set : RangeFromInterval(surface_cache, interval)) {
|
||||
for (const auto& surface : set.second) {
|
||||
if (surface->pixel_format == PixelFormat::Invalid) {
|
||||
LOG_DEBUG(HW_GPU, "Surface {:#x} found with invalid pixel format",
|
||||
surface->addr);
|
||||
LOG_DEBUG(HW_GPU, "Surface {:#x} found with invalid pixel format", surface->addr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
template <class S>
|
||||
class SurfaceBase : public SurfaceParams, public std::enable_shared_from_this<S> {
|
||||
using Watcher = SurfaceWatcher<S>;
|
||||
|
||||
public:
|
||||
SurfaceBase(SurfaceParams& params) : SurfaceParams{params} {}
|
||||
virtual ~SurfaceBase() = default;
|
||||
@ -53,7 +54,8 @@ public:
|
||||
/// Returns true when this surface can be used to fill the fill_interval of dest_surface
|
||||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
|
||||
|
||||
/// Returns true when copy_interval of dest_surface can be validated by copying from this surface
|
||||
/// Returns true when copy_interval of dest_surface can be validated by copying from this
|
||||
/// surface
|
||||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
|
||||
|
||||
/// Returns the region of the biggest valid rectange within interval
|
||||
@ -93,7 +95,8 @@ public:
|
||||
};
|
||||
|
||||
template <class S>
|
||||
bool SurfaceBase<S>::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const {
|
||||
bool SurfaceBase<S>::CanFill(const SurfaceParams& dest_surface,
|
||||
SurfaceInterval fill_interval) const {
|
||||
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
|
||||
boost::icl::first(fill_interval) >= addr &&
|
||||
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
|
||||
@ -139,13 +142,15 @@ template <class S>
|
||||
SurfaceInterval SurfaceBase<S>::GetCopyableInterval(const SurfaceParams& params) const {
|
||||
SurfaceInterval result{};
|
||||
const u32 tile_align = params.BytesInPixels(params.is_tiled ? 8 * 8 : 1);
|
||||
const auto valid_regions = SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
|
||||
const auto valid_regions =
|
||||
SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
|
||||
|
||||
for (auto& valid_interval : valid_regions) {
|
||||
const SurfaceInterval aligned_interval{
|
||||
params.addr + Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
|
||||
params.addr + Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)
|
||||
};
|
||||
params.addr +
|
||||
Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
|
||||
params.addr +
|
||||
Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)};
|
||||
|
||||
if (params.BytesInPixels(tile_align) > boost::icl::length(valid_interval) ||
|
||||
boost::icl::length(aligned_interval) == 0) {
|
||||
@ -155,8 +160,10 @@ SurfaceInterval SurfaceBase<S>::GetCopyableInterval(const SurfaceParams& params)
|
||||
// Get the rectangle within aligned_interval
|
||||
const u32 stride_bytes = params.BytesInPixels(params.stride) * (params.is_tiled ? 8 : 1);
|
||||
SurfaceInterval rect_interval{
|
||||
params.addr + Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
|
||||
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr, stride_bytes),
|
||||
params.addr +
|
||||
Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
|
||||
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr,
|
||||
stride_bytes),
|
||||
};
|
||||
|
||||
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
|
||||
|
@ -15,10 +15,7 @@ namespace VideoCore {
|
||||
|
||||
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
|
||||
|
||||
enum class TextureType {
|
||||
Texture2D = 0,
|
||||
CubeMap = 1
|
||||
};
|
||||
enum class TextureType { Texture2D = 0, CubeMap = 1 };
|
||||
|
||||
class SurfaceParams {
|
||||
public:
|
||||
|
@ -3,10 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/texture/texture_decode.h"
|
||||
#include "video_core/rasterizer_cache/morton_swizzle.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
#include "video_core/texture/texture_decode.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
@ -51,18 +51,16 @@ void SwizzleTexture(const SurfaceParams& swizzle_info, PAddr start_addr, PAddr e
|
||||
std::span<std::byte> source_linear, std::span<std::byte> dest_tiled) {
|
||||
const u32 func_index = static_cast<u32>(swizzle_info.pixel_format);
|
||||
const MortonFunc SwizzleImpl = SWIZZLE_TABLE[func_index];
|
||||
SwizzleImpl(swizzle_info.width, swizzle_info.height,
|
||||
start_addr - swizzle_info.addr, end_addr - swizzle_info.addr,
|
||||
source_linear, dest_tiled);
|
||||
SwizzleImpl(swizzle_info.width, swizzle_info.height, start_addr - swizzle_info.addr,
|
||||
end_addr - swizzle_info.addr, source_linear, dest_tiled);
|
||||
}
|
||||
|
||||
void UnswizzleTexture(const SurfaceParams& unswizzle_info, PAddr start_addr, PAddr end_addr,
|
||||
std::span<std::byte> source_tiled, std::span<std::byte> dest_linear) {
|
||||
const u32 func_index = static_cast<u32>(unswizzle_info.pixel_format);
|
||||
const MortonFunc UnswizzleImpl = UNSWIZZLE_TABLE[func_index];
|
||||
UnswizzleImpl(unswizzle_info.width, unswizzle_info.height,
|
||||
start_addr - unswizzle_info.addr, end_addr - unswizzle_info.addr,
|
||||
dest_linear, source_tiled);
|
||||
UnswizzleImpl(unswizzle_info.width, unswizzle_info.height, start_addr - unswizzle_info.addr,
|
||||
end_addr - unswizzle_info.addr, dest_linear, source_tiled);
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@ -147,7 +147,6 @@ void Driver::FindBugs() {
|
||||
if (vendor == Vendor::AMD || (vendor == Vendor::Intel && !is_linux)) {
|
||||
bugs |= DriverBug::BrokenTextureView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@ -7,13 +7,7 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
enum class Vendor {
|
||||
Unknown = 0,
|
||||
AMD = 1,
|
||||
Nvidia = 2,
|
||||
Intel = 3,
|
||||
Generic = 4
|
||||
};
|
||||
enum class Vendor { Unknown = 0, AMD = 1, Nvidia = 2, Intel = 3, Generic = 4 };
|
||||
|
||||
enum class DriverBug {
|
||||
// AMD drivers sometimes freeze when one shader stage is changed but not the others.
|
||||
|
@ -46,8 +46,8 @@ void D24S8toRGBA8::Reinterpret(const Surface& source, VideoCore::Rect2D src_rect
|
||||
if (use_texture_view) {
|
||||
temp_tex.Create();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.texture.handle, GL_DEPTH24_STENCIL8, 0, 1,
|
||||
0, 1);
|
||||
glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.texture.handle, GL_DEPTH24_STENCIL8, 0,
|
||||
1, 0, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else {
|
||||
@ -70,8 +70,8 @@ void D24S8toRGBA8::Reinterpret(const Surface& source, VideoCore::Rect2D src_rect
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
if (!use_texture_view) {
|
||||
glCopyImageSubData(source.texture.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
glCopyImageSubData(source.texture.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom,
|
||||
0, temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
@ -154,8 +154,7 @@ void RGBA4toRGB5A1::Reinterpret(const Surface& source, VideoCore::Rect2D src_rec
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
dest.texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight());
|
||||
glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight());
|
||||
|
@ -16,8 +16,8 @@ public:
|
||||
virtual ~FormatReinterpreterBase() = default;
|
||||
|
||||
virtual VideoCore::PixelFormat GetSourceFormat() const = 0;
|
||||
virtual void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect,
|
||||
const Surface& dest, VideoCore::Rect2D dst_rect) = 0;
|
||||
virtual void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect, const Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) = 0;
|
||||
};
|
||||
|
||||
using ReinterpreterList = std::vector<std::unique_ptr<FormatReinterpreterBase>>;
|
||||
@ -30,8 +30,8 @@ public:
|
||||
return VideoCore::PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect,
|
||||
const Surface& dest, VideoCore::Rect2D dst_rect) override;
|
||||
void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect, const Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) override;
|
||||
|
||||
private:
|
||||
bool use_texture_view{};
|
||||
@ -49,8 +49,8 @@ public:
|
||||
return VideoCore::PixelFormat::RGBA4;
|
||||
}
|
||||
|
||||
void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect,
|
||||
const Surface& dest, VideoCore::Rect2D dst_rect) override;
|
||||
void Reinterpret(const Surface& source, VideoCore::Rect2D src_rect, const Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) override;
|
||||
|
||||
private:
|
||||
OGLFramebuffer read_fbo;
|
||||
|
@ -39,8 +39,8 @@ static bool IsVendorIntel() {
|
||||
#endif
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& emu_window, Driver& driver)
|
||||
: driver{driver}, runtime{driver}, res_cache{*this, runtime},
|
||||
is_amd(IsVendorAmd()), vertex_buffer(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE, is_amd),
|
||||
: driver{driver}, runtime{driver}, res_cache{*this, runtime}, is_amd(IsVendorAmd()),
|
||||
vertex_buffer(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE, is_amd),
|
||||
uniform_buffer(GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE, false),
|
||||
index_buffer(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE, false),
|
||||
texture_buffer(GL_TEXTURE_BUFFER, TEXTURE_BUFFER_SIZE, false),
|
||||
@ -687,8 +687,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
|
||||
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
|
||||
.width = texture.config.width,
|
||||
.format = texture.format
|
||||
};
|
||||
.format = texture.format};
|
||||
|
||||
state.texture_cube_unit.texture_cube =
|
||||
res_cache.GetTextureCube(config)->texture.handle;
|
||||
@ -729,7 +728,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
// Making a copy to sample from eliminates this issue and seems to be fairly cheap.
|
||||
OGLTexture temp_tex;
|
||||
if (need_duplicate_texture) {
|
||||
temp_tex = runtime.Allocate(color_surface->GetScaledWidth(), color_surface->GetScaledHeight(),
|
||||
temp_tex =
|
||||
runtime.Allocate(color_surface->GetScaledWidth(), color_surface->GetScaledHeight(),
|
||||
color_surface->pixel_format, color_surface->texture_type);
|
||||
|
||||
temp_tex.CopyFrom(color_surface->texture, GL_TEXTURE_2D, color_surface->max_level + 1,
|
||||
|
@ -9,10 +9,10 @@
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/regs_lighting.h"
|
||||
#include "video_core/regs_texturing.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
|
||||
namespace Frontend {
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
|
@ -25,7 +25,8 @@ GLuint LoadShader(const char* source, GLenum type) {
|
||||
#if defined(GL_EXT_clip_cull_distance)
|
||||
#extension GL_EXT_clip_cull_distance : enable
|
||||
#endif
|
||||
)" : "#version 430 core\n";
|
||||
)"
|
||||
: "#version 430 core\n";
|
||||
|
||||
const char* debug_type;
|
||||
switch (type) {
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_runtime.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@ -15,8 +15,7 @@ constexpr FormatTuple DEFAULT_TUPLE = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
|
||||
|
||||
static constexpr std::array DEPTH_TUPLES = {
|
||||
FormatTuple{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
||||
FormatTuple{},
|
||||
FormatTuple{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
FormatTuple{}, FormatTuple{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
FormatTuple{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
||||
};
|
||||
|
||||
@ -54,13 +53,14 @@ GLbitfield MakeBufferMask(VideoCore::SurfaceType type) {
|
||||
}
|
||||
|
||||
TextureRuntime::TextureRuntime(Driver& driver)
|
||||
: driver{driver}, downloader_es{false},
|
||||
filterer{Settings::values.texture_filter_name, VideoCore::GetResolutionScaleFactor()}{
|
||||
: driver{driver}, downloader_es{false}, filterer{Settings::values.texture_filter_name,
|
||||
VideoCore::GetResolutionScaleFactor()} {
|
||||
|
||||
read_fbo.Create();
|
||||
draw_fbo.Create();
|
||||
|
||||
auto Register = [this](VideoCore::PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
auto Register = [this](VideoCore::PixelFormat dest,
|
||||
std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
const u32 dst_index = static_cast<u32>(dest);
|
||||
return reinterpreters[dst_index].push_back(std::move(obj));
|
||||
};
|
||||
@ -90,17 +90,19 @@ const StagingBuffer& TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||
// Allocate a new buffer and map the data to the host
|
||||
std::byte* data = nullptr;
|
||||
if (driver.IsOpenGLES() && driver.HasExtBufferStorage()) {
|
||||
const GLbitfield storage = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT_EXT;
|
||||
glBufferStorageEXT(target, size, nullptr, storage | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(target, 0, size, access | GL_MAP_PERSISTENT_BIT_EXT |
|
||||
GL_MAP_COHERENT_BIT_EXT));
|
||||
const GLbitfield storage =
|
||||
upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT_EXT;
|
||||
glBufferStorageEXT(target, size, nullptr,
|
||||
storage | GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(
|
||||
target, 0, size, access | GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT));
|
||||
} else if (driver.HasArbBufferStorage()) {
|
||||
const GLbitfield storage = upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT;
|
||||
glBufferStorage(target, size, nullptr, storage | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(target, 0, size, access | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT));
|
||||
const GLbitfield storage =
|
||||
upload ? GL_MAP_WRITE_BIT : GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT;
|
||||
glBufferStorage(target, size, nullptr,
|
||||
storage | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
||||
data = reinterpret_cast<std::byte*>(glMapBufferRange(
|
||||
target, 0, size, access | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT));
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@ -108,10 +110,7 @@ const StagingBuffer& TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||
glBindBuffer(target, 0);
|
||||
|
||||
StagingBuffer staging = {
|
||||
.buffer = std::move(buffer),
|
||||
.mapped = std::span{data, size},
|
||||
.size = size
|
||||
};
|
||||
.buffer = std::move(buffer), .mapped = std::span{data, size}, .size = size};
|
||||
|
||||
const auto& it = search.emplace(std::move(staging));
|
||||
return *it;
|
||||
@ -134,8 +133,8 @@ const FormatTuple& TextureRuntime::GetFormatTuple(VideoCore::PixelFormat pixel_f
|
||||
return DEFAULT_TUPLE;
|
||||
}
|
||||
|
||||
void TextureRuntime::FormatConvert(const Surface& surface, bool upload,
|
||||
std::span<std::byte> source, std::span<std::byte> dest) {
|
||||
void TextureRuntime::FormatConvert(const Surface& surface, bool upload, std::span<std::byte> source,
|
||||
std::span<std::byte> dest) {
|
||||
const VideoCore::PixelFormat format = surface.pixel_format;
|
||||
if (format == VideoCore::PixelFormat::RGBA8 && driver.IsOpenGLES()) {
|
||||
return Pica::Texture::ConvertABGRToRGBA(source, dest);
|
||||
@ -153,11 +152,7 @@ OGLTexture TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
const GLenum target =
|
||||
type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
||||
const VideoCore::HostTextureTag key = {
|
||||
.format = format,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = layers
|
||||
};
|
||||
.format = format, .width = width, .height = height, .layers = layers};
|
||||
|
||||
// Attempt to recycle an unused texture
|
||||
if (auto it = texture_recycler.find(key); it != texture_recycler.end()) {
|
||||
@ -177,8 +172,8 @@ OGLTexture TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(target, texture.handle);
|
||||
|
||||
glTexStorage2D(target, std::bit_width(std::max(width, height)),
|
||||
tuple.internal_format, width, height);
|
||||
glTexStorage2D(target, std::bit_width(std::max(width, height)), tuple.internal_format, width,
|
||||
height);
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@ -250,23 +245,22 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy) {
|
||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureCopy& copy) {
|
||||
// Emulate texture copy with blit for now
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = copy.src_level,
|
||||
.dst_level = copy.dst_level,
|
||||
.src_layer = copy.src_layer,
|
||||
.dst_layer = copy.dst_layer,
|
||||
.src_rect = {copy.src_offset.x, copy.extent.height,
|
||||
copy.extent.width, copy.src_offset.x},
|
||||
.dst_rect = {copy.dst_offset.x, copy.extent.height,
|
||||
copy.extent.width, copy.dst_offset.x}
|
||||
};
|
||||
.src_rect = {copy.src_offset.x, copy.extent.height, copy.extent.width, copy.src_offset.x},
|
||||
.dst_rect = {copy.dst_offset.x, copy.extent.height, copy.extent.width, copy.dst_offset.x}};
|
||||
|
||||
return BlitTextures(source, dest, blit);
|
||||
}
|
||||
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) {
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
@ -275,12 +269,15 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCor
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.Apply();
|
||||
|
||||
const GLenum src_textarget = source.texture_type == VideoCore::TextureType::CubeMap ?
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : GL_TEXTURE_2D;
|
||||
BindFramebuffer(GL_READ_FRAMEBUFFER, blit.src_level, src_textarget, source.type, source.texture);
|
||||
const GLenum src_textarget = source.texture_type == VideoCore::TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer
|
||||
: GL_TEXTURE_2D;
|
||||
BindFramebuffer(GL_READ_FRAMEBUFFER, blit.src_level, src_textarget, source.type,
|
||||
source.texture);
|
||||
|
||||
const GLenum dst_textarget = dest.texture_type == VideoCore::TextureType::CubeMap ?
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : GL_TEXTURE_2D;
|
||||
const GLenum dst_textarget = dest.texture_type == VideoCore::TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer
|
||||
: GL_TEXTURE_2D;
|
||||
BindFramebuffer(GL_DRAW_FRAMEBUFFER, blit.dst_level, dst_textarget, dest.type, dest.texture);
|
||||
|
||||
// TODO (wwylele): use GL_NEAREST for shadow map texture
|
||||
@ -290,9 +287,9 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCor
|
||||
// inconsistent scale.
|
||||
const GLbitfield buffer_mask = MakeBufferMask(source.type);
|
||||
const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST;
|
||||
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right, blit.src_rect.top,
|
||||
blit.dst_rect.left, blit.dst_rect.bottom, blit.dst_rect.right, blit.dst_rect.top,
|
||||
buffer_mask, filter);
|
||||
glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right,
|
||||
blit.src_rect.top, blit.dst_rect.left, blit.dst_rect.bottom,
|
||||
blit.dst_rect.right, blit.dst_rect.top, buffer_mask, filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -310,7 +307,8 @@ void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(VideoCore::PixelFormat dest_format) const {
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(
|
||||
VideoCore::PixelFormat dest_format) const {
|
||||
return reinterpreters[static_cast<u32>(dest_format)];
|
||||
}
|
||||
|
||||
@ -332,7 +330,8 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge
|
||||
break;
|
||||
case VideoCore::SurfaceType::DepthStencil:
|
||||
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, 0, 0);
|
||||
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, texture.handle, level);
|
||||
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, texture.handle,
|
||||
level);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
@ -342,7 +341,8 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge
|
||||
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
|
||||
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, driver{runtime.GetDriver()} {
|
||||
if (pixel_format != VideoCore::PixelFormat::Invalid) {
|
||||
texture = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format, texture_type);
|
||||
texture = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format,
|
||||
texture_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,8 +352,7 @@ Surface::~Surface() {
|
||||
.format = pixel_format,
|
||||
.width = GetScaledWidth(),
|
||||
.height = GetScaledHeight(),
|
||||
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u
|
||||
};
|
||||
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u};
|
||||
|
||||
runtime.texture_recycler.emplace(tag, std::move(texture));
|
||||
}
|
||||
@ -380,11 +379,9 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingBu
|
||||
glBindTexture(GL_TEXTURE_2D, texture.handle);
|
||||
|
||||
const auto& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level,
|
||||
upload.texture_rect.left, upload.texture_rect.bottom,
|
||||
upload.texture_rect.GetWidth(),
|
||||
upload.texture_rect.GetHeight(),
|
||||
tuple.format, tuple.type, 0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, upload.texture_rect.left,
|
||||
upload.texture_rect.bottom, upload.texture_rect.GetWidth(),
|
||||
upload.texture_rect.GetHeight(), tuple.format, tuple.type, 0);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
@ -412,7 +409,8 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
|
||||
if (is_scaled) {
|
||||
ScaledDownload(download);
|
||||
} else {
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
|
||||
texture);
|
||||
|
||||
const auto& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
glReadPixels(download.texture_rect.left, download.texture_rect.bottom,
|
||||
@ -429,15 +427,16 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) {
|
||||
const u32 rect_height = download.texture_rect.GetHeight();
|
||||
|
||||
// Allocate an unscaled texture that fits the download rectangle to use as a blit destination
|
||||
OGLTexture unscaled_tex = runtime.Allocate(rect_width, rect_height, pixel_format,
|
||||
VideoCore::TextureType::Texture2D);
|
||||
OGLTexture unscaled_tex =
|
||||
runtime.Allocate(rect_width, rect_height, pixel_format, VideoCore::TextureType::Texture2D);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
|
||||
texture);
|
||||
|
||||
// Blit the scaled rectangle to the unscaled texture
|
||||
const VideoCore::Rect2D scaled_rect = download.texture_rect * res_scale;
|
||||
glBlitFramebuffer(scaled_rect.left, scaled_rect.bottom, scaled_rect.right, scaled_rect.top,
|
||||
0, 0, rect_width, rect_height, MakeBufferMask(type), GL_LINEAR);
|
||||
glBlitFramebuffer(scaled_rect.left, scaled_rect.bottom, scaled_rect.right, scaled_rect.top, 0,
|
||||
0, rect_width, rect_height, MakeBufferMask(type), GL_LINEAR);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle);
|
||||
@ -445,8 +444,8 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) {
|
||||
const auto& tuple = runtime.GetFormatTuple(pixel_format);
|
||||
if (driver.IsOpenGLES()) {
|
||||
const auto& downloader_es = runtime.GetDownloaderES();
|
||||
downloader_es.GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
|
||||
rect_height, rect_width, 0);
|
||||
downloader_es.GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, rect_height,
|
||||
rect_width, 0);
|
||||
} else {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, 0);
|
||||
}
|
||||
@ -456,8 +455,8 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload) {
|
||||
const u32 rect_width = upload.texture_rect.GetWidth();
|
||||
const u32 rect_height = upload.texture_rect.GetHeight();
|
||||
|
||||
OGLTexture unscaled_tex = runtime.Allocate(rect_width, rect_height, pixel_format,
|
||||
VideoCore::TextureType::Texture2D);
|
||||
OGLTexture unscaled_tex =
|
||||
runtime.Allocate(rect_width, rect_height, pixel_format, VideoCore::TextureType::Texture2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle);
|
||||
|
||||
@ -470,13 +469,13 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload) {
|
||||
const auto& filterer = runtime.GetFilterer();
|
||||
if (!filterer.Filter(unscaled_tex, unscaled_rect, texture, scaled_rect, type)) {
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type,
|
||||
texture);
|
||||
|
||||
// If filtering fails, resort to normal blitting
|
||||
glBlitFramebuffer(0, 0, rect_width, rect_height,
|
||||
upload.texture_rect.left, upload.texture_rect.bottom,
|
||||
upload.texture_rect.right, upload.texture_rect.top,
|
||||
MakeBufferMask(type), GL_LINEAR);
|
||||
glBlitFramebuffer(0, 0, rect_width, rect_height, upload.texture_rect.left,
|
||||
upload.texture_rect.bottom, upload.texture_rect.right,
|
||||
upload.texture_rect.top, MakeBufferMask(type), GL_LINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <span>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
#include "video_core/renderer_opengl/texture_downloader_es.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@ -59,6 +59,7 @@ class Surface;
|
||||
*/
|
||||
class TextureRuntime {
|
||||
friend class Surface;
|
||||
|
||||
public:
|
||||
TextureRuntime(Driver& driver);
|
||||
~TextureRuntime() = default;
|
||||
@ -72,8 +73,8 @@ public:
|
||||
void Finish() const {}
|
||||
|
||||
/// Performs required format convertions on the staging data
|
||||
void FormatConvert(const Surface& surface, bool upload,
|
||||
std::span<std::byte> source, std::span<std::byte> dest);
|
||||
void FormatConvert(const Surface& surface, bool upload, std::span<std::byte> source,
|
||||
std::span<std::byte> dest);
|
||||
|
||||
/// Allocates an OpenGL texture with the specified dimentions and format
|
||||
OGLTexture Allocate(u32 width, u32 height, VideoCore::PixelFormat format,
|
||||
@ -98,8 +99,8 @@ public:
|
||||
|
||||
private:
|
||||
/// Returns the framebuffer used for texture downloads
|
||||
void BindFramebuffer(GLenum target, GLint level, GLenum textarget,
|
||||
VideoCore::SurfaceType type, OGLTexture& texture) const;
|
||||
void BindFramebuffer(GLenum target, GLint level, GLenum textarget, VideoCore::SurfaceType type,
|
||||
OGLTexture& texture) const;
|
||||
|
||||
/// Returns the OpenGL driver class
|
||||
const Driver& GetDriver() const {
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include <array>
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/frame_dumper_opengl.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
|
@ -18,8 +18,7 @@ namespace OpenGL {
|
||||
|
||||
static constexpr std::array DEPTH_TUPLES_HACK = {
|
||||
FormatTuple{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
||||
FormatTuple{},
|
||||
FormatTuple{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
FormatTuple{}, FormatTuple{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
||||
FormatTuple{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@ class OpenGLState;
|
||||
|
||||
class TextureDownloaderES {
|
||||
static constexpr u16 MAX_SIZE = 1024;
|
||||
|
||||
public:
|
||||
TextureDownloaderES(bool enable_depth_stencil);
|
||||
|
||||
@ -22,16 +23,16 @@ public:
|
||||
* Depth texture download assumes that the texture's format tuple matches what is found
|
||||
* OpenGL::depth_format_tuples
|
||||
*/
|
||||
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type,
|
||||
GLint height, GLint width, void* pixels) const;
|
||||
void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, GLint height,
|
||||
GLint width, void* pixels) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* OpenGL ES does not support glReadBuffer for depth/stencil formats.
|
||||
* This gets around it by converting to a Red surface before downloading
|
||||
*/
|
||||
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type,
|
||||
GLint height, GLint width) const;
|
||||
GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLint height,
|
||||
GLint width) const;
|
||||
|
||||
/// Self tests for the texture downloader
|
||||
void Test();
|
||||
|
@ -63,7 +63,8 @@ bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> s
|
||||
VideoCore::SurfaceType type) const {
|
||||
|
||||
// Depth/Stencil texture filtering is not supported for now
|
||||
if (IsNull() || (type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) {
|
||||
if (IsNull() ||
|
||||
(type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -19,15 +19,10 @@ struct FilterInfo {
|
||||
};
|
||||
|
||||
inline FilterInfo TextureFilterMode(TextureFilter mag, TextureFilter min, TextureFilter mip) {
|
||||
constexpr std::array filter_table = {
|
||||
vk::Filter::eNearest,
|
||||
vk::Filter::eLinear
|
||||
};
|
||||
constexpr std::array filter_table = {vk::Filter::eNearest, vk::Filter::eLinear};
|
||||
|
||||
constexpr std::array mipmap_table = {
|
||||
vk::SamplerMipmapMode::eNearest,
|
||||
vk::SamplerMipmapMode::eLinear
|
||||
};
|
||||
constexpr std::array mipmap_table = {vk::SamplerMipmapMode::eNearest,
|
||||
vk::SamplerMipmapMode::eLinear};
|
||||
|
||||
return FilterInfo{filter_table.at(mag), filter_table.at(min), mipmap_table.at(mip)};
|
||||
}
|
||||
@ -129,9 +124,9 @@ inline vk::BlendFactor BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
|
||||
vk::BlendFactor::eDstAlpha, // BlendFactor::DestAlpha
|
||||
vk::BlendFactor::eOneMinusDstAlpha, // BlendFactor::OneMinusDestAlpha
|
||||
vk::BlendFactor::eConstantColor, // BlendFactor::ConstantColor
|
||||
vk::BlendFactor::eOneMinusConstantColor,// BlendFactor::OneMinusConstantColor
|
||||
vk::BlendFactor::eOneMinusConstantColor, // BlendFactor::OneMinusConstantColor
|
||||
vk::BlendFactor::eConstantAlpha, // BlendFactor::ConstantAlpha
|
||||
vk::BlendFactor::eOneMinusConstantAlpha,// BlendFactor::OneMinusConstantAlpha
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // BlendFactor::OneMinusConstantAlpha
|
||||
vk::BlendFactor::eSrcAlphaSaturate, // BlendFactor::SourceAlphaSaturate
|
||||
}};
|
||||
|
||||
@ -281,4 +276,4 @@ inline Common::Vec3f LightColor(const Pica::LightingRegs::LightColor& color) {
|
||||
return Common::Vec3u{color.r, color.g, color.b} / 255.0f;
|
||||
}
|
||||
|
||||
} // namespace PicaToGL
|
||||
} // namespace PicaToVK
|
||||
|
@ -140,12 +140,11 @@ void main() {
|
||||
}
|
||||
)";
|
||||
|
||||
|
||||
/// Vertex structure that the drawn screen rectangles are composed of.
|
||||
struct ScreenRectVertex {
|
||||
ScreenRectVertex() = default;
|
||||
ScreenRectVertex(float x, float y, float u, float v) :
|
||||
position{Common::MakeVec(x, y)}, tex_coord{Common::MakeVec(u, v)} {}
|
||||
ScreenRectVertex(float x, float y, float u, float v)
|
||||
: position{Common::MakeVec(x, y)}, tex_coord{Common::MakeVec(u, v)} {}
|
||||
|
||||
Common::Vec2f position;
|
||||
Common::Vec2f tex_coord;
|
||||
@ -154,11 +153,12 @@ struct ScreenRectVertex {
|
||||
constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192;
|
||||
|
||||
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
||||
: RendererBase{window}, instance{window, Settings::values.physical_device, Settings::values.renderer_debug},
|
||||
scheduler{instance, *this},
|
||||
renderpass_cache{instance, scheduler}, runtime{instance, scheduler, renderpass_cache},
|
||||
swapchain{instance, renderpass_cache},
|
||||
vertex_buffer{instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}} {
|
||||
: RendererBase{window}, instance{window, Settings::values.physical_device,
|
||||
Settings::values.renderer_debug},
|
||||
scheduler{instance, *this}, renderpass_cache{instance, scheduler},
|
||||
runtime{instance, scheduler, renderpass_cache}, swapchain{instance, renderpass_cache},
|
||||
vertex_buffer{
|
||||
instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}} {
|
||||
|
||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||
@ -192,8 +192,7 @@ RendererVulkan::~RendererVulkan() {
|
||||
.format = VideoCore::PixelFormatFromGPUPixelFormat(info.texture.format),
|
||||
.width = info.texture.width,
|
||||
.height = info.texture.height,
|
||||
.layers = 1
|
||||
};
|
||||
.layers = 1};
|
||||
|
||||
runtime.Recycle(tag, std::move(info.texture.alloc));
|
||||
}
|
||||
@ -207,8 +206,8 @@ VideoCore::ResultStatus RendererVulkan::Init() {
|
||||
BuildPipelines();
|
||||
|
||||
// Create the rasterizer
|
||||
rasterizer = std::make_unique<RasterizerVulkan>(render_window, instance, scheduler,
|
||||
runtime, renderpass_cache);
|
||||
rasterizer = std::make_unique<RasterizerVulkan>(render_window, instance, scheduler, runtime,
|
||||
renderpass_cache);
|
||||
|
||||
return VideoCore::ResultStatus::Success;
|
||||
}
|
||||
@ -237,13 +236,8 @@ void RendererVulkan::PrepareRendertarget() {
|
||||
|
||||
if (color_fill.is_enabled) {
|
||||
const vk::ClearColorValue clear_color = {
|
||||
.float32 = std::array{
|
||||
color_fill.color_r / 255.0f,
|
||||
color_fill.color_g / 255.0f,
|
||||
color_fill.color_b / 255.0f,
|
||||
1.0f
|
||||
}
|
||||
};
|
||||
.float32 = std::array{color_fill.color_r / 255.0f, color_fill.color_g / 255.0f,
|
||||
color_fill.color_b / 255.0f, 1.0f}};
|
||||
|
||||
const vk::ImageSubresourceRange range = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
@ -255,9 +249,10 @@ void RendererVulkan::PrepareRendertarget() {
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
TextureInfo& texture = screen_infos[i].texture;
|
||||
runtime.Transition(command_buffer, texture.alloc, vk::ImageLayout::eTransferDstOptimal, 0, texture.alloc.levels);
|
||||
command_buffer.clearColorImage(texture.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
clear_color, range);
|
||||
runtime.Transition(command_buffer, texture.alloc, vk::ImageLayout::eTransferDstOptimal,
|
||||
0, texture.alloc.levels);
|
||||
command_buffer.clearColorImage(
|
||||
texture.alloc.image, vk::ImageLayout::eTransferDstOptimal, clear_color, range);
|
||||
} else {
|
||||
TextureInfo& texture = screen_infos[i].texture;
|
||||
if (texture.width != framebuffer.width || texture.height != framebuffer.height ||
|
||||
@ -280,7 +275,8 @@ void RendererVulkan::PrepareRendertarget() {
|
||||
|
||||
void RendererVulkan::BeginRendering() {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, present_pipelines[current_pipeline]);
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics,
|
||||
present_pipelines[current_pipeline]);
|
||||
|
||||
std::array<vk::DescriptorImageInfo, 4> present_textures;
|
||||
for (std::size_t i = 0; i < screen_infos.size(); i++) {
|
||||
@ -288,38 +284,29 @@ void RendererVulkan::BeginRendering() {
|
||||
present_textures[i] = vk::DescriptorImageInfo{
|
||||
.imageView = info.display_texture ? info.display_texture->image_view
|
||||
: info.texture.alloc.image_view,
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
|
||||
};
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
}
|
||||
|
||||
present_textures[3] = vk::DescriptorImageInfo{
|
||||
.sampler = present_samplers[current_sampler]
|
||||
};
|
||||
present_textures[3] = vk::DescriptorImageInfo{.sampler = present_samplers[current_sampler]};
|
||||
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {
|
||||
.descriptorPool = scheduler.GetDescriptorPool(),
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {.descriptorPool =
|
||||
scheduler.GetDescriptorPool(),
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &present_descriptor_layout
|
||||
};
|
||||
.pSetLayouts = &present_descriptor_layout};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
vk::DescriptorSet set = device.allocateDescriptorSets(alloc_info)[0];
|
||||
device.updateDescriptorSetWithTemplate(set, present_update_template, present_textures[0]);
|
||||
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, present_pipeline_layout,
|
||||
0, 1, &set, 0, nullptr);
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, present_pipeline_layout, 0,
|
||||
1, &set, 0, nullptr);
|
||||
|
||||
const vk::ClearValue clear_value = {
|
||||
.color = clear_color
|
||||
};
|
||||
const vk::ClearValue clear_value = {.color = clear_color};
|
||||
|
||||
const vk::RenderPassBeginInfo begin_info = {
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass(),
|
||||
.framebuffer = swapchain.GetFramebuffer(),
|
||||
.renderArea = vk::Rect2D{
|
||||
.offset = {0, 0},
|
||||
.extent = swapchain.GetExtent()
|
||||
},
|
||||
.renderArea = vk::Rect2D{.offset = {0, 0}, .extent = swapchain.GetExtent()},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear_value,
|
||||
};
|
||||
@ -352,7 +339,8 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
|
||||
// only allows rows to have a memory alignement of 4.
|
||||
ASSERT(pixel_stride % 4 == 0);
|
||||
|
||||
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast<u32>(pixel_stride), screen_info)) {
|
||||
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr,
|
||||
static_cast<u32>(pixel_stride), screen_info)) {
|
||||
ASSERT(false);
|
||||
// Reset the screen info's display texture to its own permanent texture
|
||||
/*screen_info.display_texture = &screen_info.texture;
|
||||
@ -370,10 +358,10 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
|
||||
|
||||
void RendererVulkan::CompileShaders() {
|
||||
vk::Device device = instance.GetDevice();
|
||||
present_vertex_shader = Compile(vertex_shader, vk::ShaderStageFlagBits::eVertex,
|
||||
device, ShaderOptimization::Debug);
|
||||
present_shaders[0] = Compile(fragment_shader, vk::ShaderStageFlagBits::eFragment,
|
||||
device, ShaderOptimization::Debug);
|
||||
present_vertex_shader =
|
||||
Compile(vertex_shader, vk::ShaderStageFlagBits::eVertex, device, ShaderOptimization::Debug);
|
||||
present_shaders[0] = Compile(fragment_shader, vk::ShaderStageFlagBits::eFragment, device,
|
||||
ShaderOptimization::Debug);
|
||||
present_shaders[1] = Compile(fragment_shader_anaglyph, vk::ShaderStageFlagBits::eFragment,
|
||||
device, ShaderOptimization::Debug);
|
||||
present_shaders[2] = Compile(fragment_shader_interlaced, vk::ShaderStageFlagBits::eFragment,
|
||||
@ -393,8 +381,7 @@ void RendererVulkan::CompileShaders() {
|
||||
.compareEnable = false,
|
||||
.compareOp = vk::CompareOp::eAlways,
|
||||
.borderColor = vk::BorderColor::eIntOpaqueBlack,
|
||||
.unnormalizedCoordinates = false
|
||||
};
|
||||
.unnormalizedCoordinates = false};
|
||||
|
||||
present_samplers[i] = device.createSampler(sampler_info);
|
||||
}
|
||||
@ -402,53 +389,41 @@ void RendererVulkan::CompileShaders() {
|
||||
|
||||
void RendererVulkan::BuildLayouts() {
|
||||
const std::array present_layout_bindings = {
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 0,
|
||||
vk::DescriptorSetLayoutBinding{.binding = 0,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.descriptorCount = 3,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eFragment
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eFragment},
|
||||
vk::DescriptorSetLayoutBinding{.binding = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampler,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eFragment
|
||||
}
|
||||
};
|
||||
.stageFlags = vk::ShaderStageFlagBits::eFragment}};
|
||||
|
||||
const vk::DescriptorSetLayoutCreateInfo present_layout_info = {
|
||||
.bindingCount = static_cast<u32>(present_layout_bindings.size()),
|
||||
.pBindings = present_layout_bindings.data()
|
||||
};
|
||||
.pBindings = present_layout_bindings.data()};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
present_descriptor_layout = device.createDescriptorSetLayout(present_layout_info);
|
||||
|
||||
const std::array update_template_entries = {
|
||||
vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = 0,
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 3,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.offset = 0,
|
||||
.stride = sizeof(vk::DescriptorImageInfo)
|
||||
},
|
||||
vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = 1,
|
||||
.stride = sizeof(vk::DescriptorImageInfo)},
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampler,
|
||||
.offset = 3 * sizeof(vk::DescriptorImageInfo),
|
||||
.stride = 0
|
||||
}
|
||||
};
|
||||
.stride = 0}};
|
||||
|
||||
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
|
||||
.descriptorUpdateEntryCount = static_cast<u32>(update_template_entries.size()),
|
||||
.pDescriptorUpdateEntries = update_template_entries.data(),
|
||||
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
|
||||
.descriptorSetLayout = present_descriptor_layout
|
||||
};
|
||||
.descriptorSetLayout = present_descriptor_layout};
|
||||
|
||||
present_update_template = device.createDescriptorUpdateTemplate(template_info);
|
||||
|
||||
@ -458,49 +433,37 @@ void RendererVulkan::BuildLayouts() {
|
||||
.size = sizeof(PresentUniformData),
|
||||
};
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = 1,
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {.setLayoutCount = 1,
|
||||
.pSetLayouts = &present_descriptor_layout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &push_range
|
||||
};
|
||||
.pPushConstantRanges = &push_range};
|
||||
|
||||
present_pipeline_layout = device.createPipelineLayout(layout_info);
|
||||
}
|
||||
|
||||
void RendererVulkan::BuildPipelines() {
|
||||
const vk::VertexInputBindingDescription binding = {
|
||||
.binding = 0,
|
||||
const vk::VertexInputBindingDescription binding = {.binding = 0,
|
||||
.stride = sizeof(ScreenRectVertex),
|
||||
.inputRate = vk::VertexInputRate::eVertex
|
||||
};
|
||||
.inputRate = vk::VertexInputRate::eVertex};
|
||||
|
||||
const std::array attributes = {
|
||||
vk::VertexInputAttributeDescription{
|
||||
.location = 0,
|
||||
vk::VertexInputAttributeDescription{.location = 0,
|
||||
.binding = 0,
|
||||
.format = vk::Format::eR32G32Sfloat,
|
||||
.offset = offsetof(ScreenRectVertex, position)
|
||||
},
|
||||
vk::VertexInputAttributeDescription{
|
||||
.location = 1,
|
||||
.offset = offsetof(ScreenRectVertex, position)},
|
||||
vk::VertexInputAttributeDescription{.location = 1,
|
||||
.binding = 0,
|
||||
.format = vk::Format::eR32G32Sfloat,
|
||||
.offset = offsetof(ScreenRectVertex, tex_coord)
|
||||
}
|
||||
};
|
||||
.offset = offsetof(ScreenRectVertex, tex_coord)}};
|
||||
|
||||
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = &binding,
|
||||
.vertexAttributeDescriptionCount = static_cast<u32>(attributes.size()),
|
||||
.pVertexAttributeDescriptions = attributes.data()
|
||||
};
|
||||
.pVertexAttributeDescriptions = attributes.data()};
|
||||
|
||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
||||
.topology = vk::PrimitiveTopology::eTriangleStrip,
|
||||
.primitiveRestartEnable = false
|
||||
};
|
||||
.topology = vk::PrimitiveTopology::eTriangleStrip, .primitiveRestartEnable = false};
|
||||
|
||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
||||
.depthClampEnable = false,
|
||||
@ -508,26 +471,21 @@ void RendererVulkan::BuildPipelines() {
|
||||
.cullMode = vk::CullModeFlagBits::eNone,
|
||||
.frontFace = vk::FrontFace::eClockwise,
|
||||
.depthBiasEnable = false,
|
||||
.lineWidth = 1.0f
|
||||
};
|
||||
.lineWidth = 1.0f};
|
||||
|
||||
const vk::PipelineMultisampleStateCreateInfo multisampling = {
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||
.sampleShadingEnable = false
|
||||
};
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = false};
|
||||
|
||||
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
|
||||
.blendEnable = false,
|
||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
};
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
|
||||
|
||||
const vk::PipelineColorBlendStateCreateInfo color_blending = {
|
||||
.logicOpEnable = false,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorblend_attachment,
|
||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}
|
||||
};
|
||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
|
||||
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
const vk::Rect2D placeholder_scissor = vk::Rect2D{{0, 0}, {1, 1}};
|
||||
@ -538,36 +496,27 @@ void RendererVulkan::BuildPipelines() {
|
||||
.pScissors = &placeholder_scissor,
|
||||
};
|
||||
|
||||
const std::array dynamic_states = {
|
||||
vk::DynamicState::eViewport,
|
||||
vk::DynamicState::eScissor
|
||||
};
|
||||
const std::array dynamic_states = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
|
||||
|
||||
const vk::PipelineDynamicStateCreateInfo dynamic_info = {
|
||||
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
|
||||
.pDynamicStates = dynamic_states.data()
|
||||
};
|
||||
.pDynamicStates = dynamic_states.data()};
|
||||
|
||||
const vk::PipelineDepthStencilStateCreateInfo depth_info = {
|
||||
.depthTestEnable = false,
|
||||
const vk::PipelineDepthStencilStateCreateInfo depth_info = {.depthTestEnable = false,
|
||||
.depthWriteEnable = false,
|
||||
.depthCompareOp = vk::CompareOp::eAlways,
|
||||
.depthCompareOp =
|
||||
vk::CompareOp::eAlways,
|
||||
.depthBoundsTestEnable = false,
|
||||
.stencilTestEnable = false
|
||||
};
|
||||
.stencilTestEnable = false};
|
||||
|
||||
for (u32 i = 0; i < PRESENT_PIPELINES; i++) {
|
||||
const std::array shader_stages = {
|
||||
vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
vk::PipelineShaderStageCreateInfo{.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
.module = present_vertex_shader,
|
||||
.pName = "main"
|
||||
},
|
||||
vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||
.pName = "main"},
|
||||
vk::PipelineShaderStageCreateInfo{.stage = vk::ShaderStageFlagBits::eFragment,
|
||||
.module = present_shaders[i],
|
||||
.pName = "main"
|
||||
},
|
||||
.pName = "main"},
|
||||
};
|
||||
|
||||
const vk::GraphicsPipelineCreateInfo pipeline_info = {
|
||||
@ -582,8 +531,7 @@ void RendererVulkan::BuildPipelines() {
|
||||
.pColorBlendState = &color_blending,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.layout = present_pipeline_layout,
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass()
|
||||
};
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass()};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
if (const auto result = device.createGraphicsPipeline({}, pipeline_info);
|
||||
@ -596,10 +544,12 @@ void RendererVulkan::BuildPipelines() {
|
||||
}
|
||||
}
|
||||
|
||||
void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer) {
|
||||
void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
const GPU::Regs::FramebufferConfig& framebuffer) {
|
||||
TextureInfo old_texture = texture;
|
||||
texture = TextureInfo{
|
||||
.alloc = runtime.Allocate(framebuffer.width, framebuffer.height,
|
||||
.alloc =
|
||||
runtime.Allocate(framebuffer.width, framebuffer.height,
|
||||
VideoCore::PixelFormatFromGPUPixelFormat(framebuffer.color_format),
|
||||
VideoCore::TextureType::Texture2D),
|
||||
.width = framebuffer.width,
|
||||
@ -607,14 +557,14 @@ void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, const GPU
|
||||
.format = framebuffer.color_format,
|
||||
};
|
||||
|
||||
// Recyle the old texture after allocation to avoid having duplicates of the same allocation in the recycler
|
||||
// Recyle the old texture after allocation to avoid having duplicates of the same allocation in
|
||||
// the recycler
|
||||
if (old_texture.width != 0 && old_texture.height != 0) {
|
||||
const VideoCore::HostTextureTag tag = {
|
||||
.format = VideoCore::PixelFormatFromGPUPixelFormat(old_texture.format),
|
||||
.width = old_texture.width,
|
||||
.height = old_texture.height,
|
||||
.layers = 1
|
||||
};
|
||||
.layers = 1};
|
||||
|
||||
runtime.Recycle(tag, std::move(old_texture.alloc));
|
||||
}
|
||||
@ -665,15 +615,16 @@ void RendererVulkan::DrawSingleScreenRotated(u32 screen_id, float x, float y, fl
|
||||
const float width = static_cast<float>(screen_info.texture.width);
|
||||
const float height = static_cast<float>(screen_info.texture.height);
|
||||
|
||||
draw_info.i_resolution = Common::Vec4f{width * scale_factor, height * scale_factor,
|
||||
1.0f / (width * scale_factor),
|
||||
draw_info.i_resolution =
|
||||
Common::Vec4f{width * scale_factor, height * scale_factor, 1.0f / (width * scale_factor),
|
||||
1.0f / (height * scale_factor)};
|
||||
draw_info.o_resolution = Common::Vec4f{h, w, 1.0f / h, 1.0f / w};
|
||||
draw_info.screen_id_l = screen_id;
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.pushConstants(present_pipeline_layout,
|
||||
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment |
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(draw_info), &draw_info);
|
||||
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer.GetHandle(), {0});
|
||||
@ -701,20 +652,19 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w,
|
||||
const float width = static_cast<float>(screen_info.texture.width);
|
||||
const float height = static_cast<float>(screen_info.texture.height);
|
||||
|
||||
draw_info.i_resolution = Common::Vec4f{width * scale_factor, height * scale_factor,
|
||||
1.0f / (width * scale_factor),
|
||||
draw_info.i_resolution =
|
||||
Common::Vec4f{width * scale_factor, height * scale_factor, 1.0f / (width * scale_factor),
|
||||
1.0f / (height * scale_factor)};
|
||||
draw_info.o_resolution = Common::Vec4f{h, w, 1.0f / h, 1.0f / w};
|
||||
draw_info.screen_id_l = screen_id;
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.pushConstants(present_pipeline_layout,
|
||||
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment |
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(draw_info), &draw_info);
|
||||
|
||||
const vk::ClearValue clear_value = {
|
||||
.color = clear_color
|
||||
};
|
||||
const vk::ClearValue clear_value = {.color = clear_color};
|
||||
|
||||
const vk::RenderPassBeginInfo begin_info = {
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass(),
|
||||
@ -729,20 +679,18 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w,
|
||||
command_buffer.draw(4, 1, offset / sizeof(ScreenRectVertex), 0);
|
||||
}
|
||||
|
||||
void RendererVulkan::DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_id_r,
|
||||
float x, float y, float w, float h) {
|
||||
void RendererVulkan::DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_id_r, float x,
|
||||
float y, float w, float h) {
|
||||
const ScreenInfo& screen_info_l = screen_infos[screen_id_l];
|
||||
const auto& texcoords = screen_info_l.display_texcoords;
|
||||
|
||||
u32 size = sizeof(ScreenRectVertex) * 4;
|
||||
auto [ptr, offset, invalidate] = vertex_buffer.Map(size);
|
||||
|
||||
const std::array vertices = {
|
||||
ScreenRectVertex{x, y, texcoords.bottom, texcoords.left},
|
||||
const std::array vertices = {ScreenRectVertex{x, y, texcoords.bottom, texcoords.left},
|
||||
ScreenRectVertex{x + w, y, texcoords.bottom, texcoords.right},
|
||||
ScreenRectVertex{x, y + h, texcoords.top, texcoords.left},
|
||||
ScreenRectVertex{x + w, y + h, texcoords.top, texcoords.right}
|
||||
};
|
||||
ScreenRectVertex{x + w, y + h, texcoords.top, texcoords.right}};
|
||||
|
||||
std::memcpy(ptr, vertices.data(), size);
|
||||
vertex_buffer.Commit(size);
|
||||
@ -751,8 +699,8 @@ void RendererVulkan::DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_i
|
||||
const float width = static_cast<float>(screen_info_l.texture.width);
|
||||
const float height = static_cast<float>(screen_info_l.texture.height);
|
||||
|
||||
draw_info.i_resolution = Common::Vec4f{width * scale_factor, height * scale_factor,
|
||||
1.0f / (width * scale_factor),
|
||||
draw_info.i_resolution =
|
||||
Common::Vec4f{width * scale_factor, height * scale_factor, 1.0f / (width * scale_factor),
|
||||
1.0f / (height * scale_factor)};
|
||||
|
||||
draw_info.o_resolution = Common::Vec4f{h, w, 1.0f / h, 1.0f / w};
|
||||
@ -761,15 +709,16 @@ void RendererVulkan::DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_i
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.pushConstants(present_pipeline_layout,
|
||||
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment |
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(draw_info), &draw_info);
|
||||
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer.GetHandle(), {0});
|
||||
command_buffer.draw(4, 1, offset / sizeof(ScreenRectVertex), 0);
|
||||
}
|
||||
|
||||
void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r,
|
||||
float x, float y, float w, float h) {
|
||||
void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y,
|
||||
float w, float h) {
|
||||
const ScreenInfo& screen_info_l = screen_infos[screen_id_l];
|
||||
const auto& texcoords = screen_info_l.display_texcoords;
|
||||
|
||||
@ -790,8 +739,8 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r,
|
||||
const float width = static_cast<float>(screen_info_l.texture.width);
|
||||
const float height = static_cast<float>(screen_info_l.texture.height);
|
||||
|
||||
draw_info.i_resolution = Common::Vec4f{width * scale_factor, height * scale_factor,
|
||||
1.0f / (width * scale_factor),
|
||||
draw_info.i_resolution =
|
||||
Common::Vec4f{width * scale_factor, height * scale_factor, 1.0f / (width * scale_factor),
|
||||
1.0f / (height * scale_factor)};
|
||||
|
||||
draw_info.o_resolution = Common::Vec4f{w, h, 1.0f / w, 1.0f / h};
|
||||
@ -800,7 +749,8 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r,
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.pushConstants(present_pipeline_layout,
|
||||
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment |
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(draw_info), &draw_info);
|
||||
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer.GetHandle(), {0});
|
||||
@ -828,11 +778,11 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
const auto& bottom_screen = layout.bottom_screen;
|
||||
|
||||
// Set projection matrix
|
||||
//draw_info.modelview =
|
||||
// MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height), flipped);
|
||||
draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast<float>(layout.width),
|
||||
static_cast<float>(layout.height), 0.0f,
|
||||
0.f, 1.f));
|
||||
// draw_info.modelview =
|
||||
// MakeOrthographicMatrix(static_cast<float>(layout.width),
|
||||
// static_cast<float>(layout.height), flipped);
|
||||
draw_info.modelview = glm::transpose(glm::ortho(
|
||||
0.f, static_cast<float>(layout.width), static_cast<float>(layout.height), 0.0f, 0.f, 1.f));
|
||||
|
||||
const bool stereo_single_screen =
|
||||
Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
|
||||
@ -846,31 +796,29 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
if (layout.top_screen_enabled) {
|
||||
if (layout.is_rotated) {
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||
DrawSingleScreenRotated(0, top_screen.left,
|
||||
top_screen.top, top_screen.GetWidth(),
|
||||
DrawSingleScreenRotated(0, top_screen.left, top_screen.top, top_screen.GetWidth(),
|
||||
top_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
||||
DrawSingleScreenRotated(0, (float)top_screen.left / 2,
|
||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||
DrawSingleScreenRotated(0, (float)top_screen.left / 2, (float)top_screen.top,
|
||||
(float)top_screen.GetWidth() / 2,
|
||||
(float)top_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreenRotated(1,
|
||||
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||
DrawSingleScreenRotated(1, ((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||
(float)top_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
|
||||
DrawSingleScreenRotated(0, layout.top_screen.left,
|
||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
||||
DrawSingleScreenRotated(0, layout.top_screen.left, layout.top_screen.top,
|
||||
layout.top_screen.GetWidth(),
|
||||
layout.top_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreenRotated(1,
|
||||
layout.cardboard.top_screen_right_eye +
|
||||
((float)layout.width / 2),
|
||||
DrawSingleScreenRotated(
|
||||
1, layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
|
||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
||||
layout.top_screen.GetHeight());
|
||||
} else if (stereo_single_screen) {
|
||||
DrawSingleScreenStereoRotated(0, 1, (float)top_screen.left, (float)top_screen.top,
|
||||
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
||||
(float)top_screen.GetWidth(),
|
||||
(float)top_screen.GetHeight());
|
||||
}
|
||||
} else {
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||
@ -880,8 +828,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
DrawSingleScreen(0, (float)top_screen.left / 2, (float)top_screen.top,
|
||||
(float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreen(1,
|
||||
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||
DrawSingleScreen(1, ((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||
(float)top_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
|
||||
@ -893,9 +840,8 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
||||
layout.top_screen.GetHeight());
|
||||
} else if (stereo_single_screen) {
|
||||
DrawSingleScreenStereo(0, 1, (float)top_screen.left,
|
||||
(float)top_screen.top, (float)top_screen.GetWidth(),
|
||||
(float)top_screen.GetHeight());
|
||||
DrawSingleScreenStereo(0, 1, (float)top_screen.left, (float)top_screen.top,
|
||||
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -904,60 +850,55 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
if (layout.bottom_screen_enabled) {
|
||||
if (layout.is_rotated) {
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||
DrawSingleScreenRotated(2, (float)bottom_screen.left,
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||
DrawSingleScreenRotated(2, (float)bottom_screen.left, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth(),
|
||||
(float)bottom_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
||||
DrawSingleScreenRotated(
|
||||
2, (float)bottom_screen.left / 2, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
|
||||
DrawSingleScreenRotated(2, (float)bottom_screen.left / 2, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth() / 2,
|
||||
(float)bottom_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreenRotated(
|
||||
2, ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||
(float)bottom_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
|
||||
DrawSingleScreenRotated(2, layout.bottom_screen.left,
|
||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||
DrawSingleScreenRotated(2, layout.bottom_screen.left, layout.bottom_screen.top,
|
||||
layout.bottom_screen.GetWidth(),
|
||||
layout.bottom_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreenRotated(2,
|
||||
layout.cardboard.bottom_screen_right_eye +
|
||||
((float)layout.width / 2),
|
||||
DrawSingleScreenRotated(
|
||||
2, layout.cardboard.bottom_screen_right_eye + ((float)layout.width / 2),
|
||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||
layout.bottom_screen.GetHeight());
|
||||
} else if (stereo_single_screen) {
|
||||
DrawSingleScreenStereoRotated(2, 2, (float)bottom_screen.left, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth(),
|
||||
(float)bottom_screen.GetHeight());
|
||||
DrawSingleScreenStereoRotated(
|
||||
2, 2, (float)bottom_screen.left, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth(), (float)bottom_screen.GetHeight());
|
||||
}
|
||||
} else {
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||
DrawSingleScreen(2, (float)bottom_screen.left,
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||
(float)bottom_screen.GetHeight());
|
||||
DrawSingleScreen(2, (float)bottom_screen.left, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth(), (float)bottom_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
||||
DrawSingleScreen(2, (float)bottom_screen.left / 2,
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||
DrawSingleScreen(2, (float)bottom_screen.left / 2, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth() / 2,
|
||||
(float)bottom_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreen(2,
|
||||
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||
DrawSingleScreen(2, ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||
(float)bottom_screen.GetHeight());
|
||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
|
||||
DrawSingleScreen(2, layout.bottom_screen.left,
|
||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||
layout.bottom_screen.GetHeight());
|
||||
DrawSingleScreen(2, layout.bottom_screen.left, layout.bottom_screen.top,
|
||||
layout.bottom_screen.GetWidth(), layout.bottom_screen.GetHeight());
|
||||
draw_info.layer = 1;
|
||||
DrawSingleScreen(2,
|
||||
layout.cardboard.bottom_screen_right_eye +
|
||||
((float)layout.width / 2),
|
||||
DrawSingleScreen(
|
||||
2, layout.cardboard.bottom_screen_right_eye + ((float)layout.width / 2),
|
||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||
layout.bottom_screen.GetHeight());
|
||||
} else if (stereo_single_screen) {
|
||||
DrawSingleScreenStereo(2, 2, (float)bottom_screen.left,
|
||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||
DrawSingleScreenStereo(2, 2, (float)bottom_screen.left, (float)bottom_screen.top,
|
||||
(float)bottom_screen.GetWidth(),
|
||||
(float)bottom_screen.GetHeight());
|
||||
}
|
||||
}
|
||||
@ -980,19 +921,14 @@ void RendererVulkan::SwapBuffers() {
|
||||
const vk::Semaphore present_ready = scheduler.GetPresentReadySemaphore();
|
||||
swapchain.AcquireNextImage(image_acquired);
|
||||
|
||||
const vk::Viewport viewport = {
|
||||
.x = 0.0f,
|
||||
const vk::Viewport viewport = {.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(layout.width),
|
||||
.height = static_cast<float>(layout.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
};
|
||||
.maxDepth = 1.0f};
|
||||
|
||||
const vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = {layout.width, layout.height}
|
||||
};
|
||||
const vk::Rect2D scissor = {.offset = {0, 0}, .extent = {layout.width, layout.height}};
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.setViewport(0, viewport);
|
||||
@ -1002,7 +938,8 @@ void RendererVulkan::SwapBuffers() {
|
||||
|
||||
for (auto& info : screen_infos) {
|
||||
auto alloc = info.display_texture ? info.display_texture : &info.texture.alloc;
|
||||
runtime.Transition(command_buffer, *alloc, vk::ImageLayout::eShaderReadOnlyOptimal, 0, alloc->levels);
|
||||
runtime.Transition(command_buffer, *alloc, vk::ImageLayout::eShaderReadOnlyOptimal, 0,
|
||||
alloc->levels);
|
||||
}
|
||||
|
||||
DrawScreens(layout, false);
|
||||
|
@ -10,9 +10,9 @@
|
||||
#include "common/math_util.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||
|
||||
namespace Layout {
|
||||
@ -81,7 +81,8 @@ private:
|
||||
void CompileShaders();
|
||||
void BuildLayouts();
|
||||
void BuildPipelines();
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer);
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
const GPU::Regs::FramebufferConfig& framebuffer);
|
||||
void ConfigureRenderPipeline();
|
||||
void PrepareRendertarget();
|
||||
void BeginRendering();
|
||||
@ -89,8 +90,10 @@ private:
|
||||
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
||||
void DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h);
|
||||
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h);
|
||||
void DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_id_r, float x, float y, float w, float h);
|
||||
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w, float h);
|
||||
void DrawSingleScreenStereoRotated(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
|
||||
float h);
|
||||
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
|
||||
float h);
|
||||
|
||||
void UpdateFramerate();
|
||||
|
||||
|
@ -41,25 +41,21 @@ constexpr vk::ImageAspectFlags GetImageAspect(vk::Format format) {
|
||||
|
||||
/// Returns a bit mask with the required usage of a format with a particular aspect
|
||||
constexpr vk::ImageUsageFlags GetImageUsage(vk::ImageAspectFlags aspect) {
|
||||
auto usage = vk::ImageUsageFlagBits::eSampled |
|
||||
vk::ImageUsageFlagBits::eTransferDst |
|
||||
auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eTransferSrc;
|
||||
|
||||
if (aspect & vk::ImageAspectFlagBits::eDepth) {
|
||||
return usage | vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
} else {
|
||||
return usage | vk::ImageUsageFlagBits::eStorage |
|
||||
vk::ImageUsageFlagBits::eColorAttachment;
|
||||
return usage | vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a bit mask with the required features of a format with a particular aspect
|
||||
constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect) {
|
||||
auto usage = vk::FormatFeatureFlagBits::eSampledImage |
|
||||
vk::FormatFeatureFlagBits::eTransferDst |
|
||||
vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitDst;
|
||||
vk::FormatFeatureFlagBits::eTransferDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitSrc | vk::FormatFeatureFlagBits::eBlitDst;
|
||||
|
||||
if (aspect & vk::ImageAspectFlagBits::eDepth) {
|
||||
return usage | vk::FormatFeatureFlagBits::eDepthStencilAttachment;
|
||||
|
@ -4,12 +4,13 @@
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "video_core/renderer_vulkan/vk_format_reinterpreter.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
D24S8toRGBA8::D24S8toRGBA8(const Instance& instance, TaskScheduler& scheduler, TextureRuntime& runtime)
|
||||
D24S8toRGBA8::D24S8toRGBA8(const Instance& instance, TaskScheduler& scheduler,
|
||||
TextureRuntime& runtime)
|
||||
: FormatReinterpreterBase{instance, scheduler, runtime}, device{instance.GetDevice()} {
|
||||
constexpr std::string_view cs_source = R"(
|
||||
#version 450 core
|
||||
@ -35,70 +36,54 @@ void main() {
|
||||
}
|
||||
|
||||
)";
|
||||
compute_shader = Compile(cs_source, vk::ShaderStageFlagBits::eCompute,
|
||||
device, ShaderOptimization::High);
|
||||
compute_shader =
|
||||
Compile(cs_source, vk::ShaderStageFlagBits::eCompute, device, ShaderOptimization::High);
|
||||
|
||||
const std::array compute_layout_bindings = {
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 0,
|
||||
vk::DescriptorSetLayoutBinding{.binding = 0,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute},
|
||||
vk::DescriptorSetLayoutBinding{.binding = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 2,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute},
|
||||
vk::DescriptorSetLayoutBinding{.binding = 2,
|
||||
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute
|
||||
}
|
||||
};
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute}};
|
||||
|
||||
const vk::DescriptorSetLayoutCreateInfo compute_layout_info = {
|
||||
.bindingCount = static_cast<u32>(compute_layout_bindings.size()),
|
||||
.pBindings = compute_layout_bindings.data()
|
||||
};
|
||||
.pBindings = compute_layout_bindings.data()};
|
||||
|
||||
descriptor_layout = device.createDescriptorSetLayout(compute_layout_info);
|
||||
|
||||
const std::array update_template_entries = {
|
||||
vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = 0,
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.offset = 0,
|
||||
.stride = sizeof(vk::DescriptorImageInfo)
|
||||
},
|
||||
vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = 1,
|
||||
.stride = sizeof(vk::DescriptorImageInfo)},
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
||||
.offset = sizeof(vk::DescriptorImageInfo),
|
||||
.stride = 0
|
||||
},
|
||||
vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = 2,
|
||||
.stride = 0},
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = 2,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||
.offset = 2 * sizeof(vk::DescriptorImageInfo),
|
||||
.stride = 0
|
||||
}
|
||||
};
|
||||
.stride = 0}};
|
||||
|
||||
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
|
||||
.descriptorUpdateEntryCount = static_cast<u32>(update_template_entries.size()),
|
||||
.pDescriptorUpdateEntries = update_template_entries.data(),
|
||||
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
|
||||
.descriptorSetLayout = descriptor_layout
|
||||
};
|
||||
.descriptorSetLayout = descriptor_layout};
|
||||
|
||||
update_template = device.createDescriptorUpdateTemplate(template_info);
|
||||
|
||||
@ -108,33 +93,25 @@ void main() {
|
||||
.size = sizeof(Common::Vec2i),
|
||||
};
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = 1,
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {.setLayoutCount = 1,
|
||||
.pSetLayouts = &descriptor_layout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &push_range
|
||||
};
|
||||
.pPushConstantRanges = &push_range};
|
||||
|
||||
compute_pipeline_layout = device.createPipelineLayout(layout_info);
|
||||
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {
|
||||
.descriptorPool = scheduler.GetPersistentDescriptorPool(),
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {.descriptorPool =
|
||||
scheduler.GetPersistentDescriptorPool(),
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &descriptor_layout
|
||||
};
|
||||
.pSetLayouts = &descriptor_layout};
|
||||
|
||||
descriptor_set = device.allocateDescriptorSets(alloc_info)[0];
|
||||
|
||||
const vk::PipelineShaderStageCreateInfo compute_stage = {
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = compute_shader,
|
||||
.pName = "main"
|
||||
};
|
||||
.stage = vk::ShaderStageFlagBits::eCompute, .module = compute_shader, .pName = "main"};
|
||||
|
||||
const vk::ComputePipelineCreateInfo compute_info = {
|
||||
.stage = compute_stage,
|
||||
.layout = compute_pipeline_layout
|
||||
};
|
||||
const vk::ComputePipelineCreateInfo compute_info = {.stage = compute_stage,
|
||||
.layout = compute_pipeline_layout};
|
||||
|
||||
if (const auto result = device.createComputePipeline({}, compute_info);
|
||||
result.result == vk::Result::eSuccess) {
|
||||
@ -153,37 +130,30 @@ D24S8toRGBA8::~D24S8toRGBA8() {
|
||||
device.destroyShaderModule(compute_shader);
|
||||
}
|
||||
|
||||
void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect,
|
||||
Surface& dest, VideoCore::Rect2D dst_rect) {
|
||||
void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
runtime.Transition(command_buffer, source.alloc, vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
0, source.alloc.levels);
|
||||
runtime.Transition(command_buffer, dest.alloc, vk::ImageLayout::eGeneral, 0, dest.alloc.levels);
|
||||
|
||||
const std::array textures = {
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetDepthView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetStencilView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = dest.GetImageView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral
|
||||
}
|
||||
};
|
||||
vk::DescriptorImageInfo{.imageView = source.GetDepthView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal},
|
||||
vk::DescriptorImageInfo{.imageView = source.GetStencilView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal},
|
||||
vk::DescriptorImageInfo{.imageView = dest.GetImageView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral}};
|
||||
|
||||
device.updateDescriptorSetWithTemplate(descriptor_set, update_template, textures[0]);
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, compute_pipeline_layout,
|
||||
0, 1, &descriptor_set, 0, nullptr);
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, compute_pipeline_layout, 0,
|
||||
1, &descriptor_set, 0, nullptr);
|
||||
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, compute_pipeline);
|
||||
|
||||
const auto src_offset = Common::MakeVec(src_rect.left, src_rect.bottom);
|
||||
command_buffer.pushConstants(compute_pipeline_layout, vk::ShaderStageFlagBits::eCompute,
|
||||
0, sizeof(Common::Vec2i), src_offset.AsArray());
|
||||
command_buffer.pushConstants(compute_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
|
||||
sizeof(Common::Vec2i), src_offset.AsArray());
|
||||
|
||||
command_buffer.dispatch(src_rect.GetWidth() / 32, src_rect.GetHeight() / 32, 1);
|
||||
}
|
||||
|
@ -16,13 +16,14 @@ class TextureRuntime;
|
||||
|
||||
class FormatReinterpreterBase {
|
||||
public:
|
||||
FormatReinterpreterBase(const Instance& instance, TaskScheduler& scheduler, TextureRuntime& runtime)
|
||||
FormatReinterpreterBase(const Instance& instance, TaskScheduler& scheduler,
|
||||
TextureRuntime& runtime)
|
||||
: instance{instance}, scheduler{scheduler}, runtime{runtime} {}
|
||||
virtual ~FormatReinterpreterBase() = default;
|
||||
|
||||
virtual VideoCore::PixelFormat GetSourceFormat() const = 0;
|
||||
virtual void Reinterpret(Surface& source, VideoCore::Rect2D src_rect,
|
||||
Surface& dest, VideoCore::Rect2D dst_rect) = 0;
|
||||
virtual void Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) = 0;
|
||||
|
||||
protected:
|
||||
const Instance& instance;
|
||||
@ -41,8 +42,8 @@ public:
|
||||
return VideoCore::PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
void Reinterpret(Surface& source, VideoCore::Rect2D src_rect,
|
||||
Surface& dest, VideoCore::Rect2D dst_rect) override;
|
||||
void Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surface& dest,
|
||||
VideoCore::Rect2D dst_rect) override;
|
||||
|
||||
private:
|
||||
vk::Device device;
|
||||
|
@ -6,8 +6,8 @@
|
||||
#include <span>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -41,28 +41,25 @@ vk::Format ToVkFormat(VideoCore::PixelFormat format) {
|
||||
Instance::Instance() {
|
||||
// Fetch instance independant function pointers
|
||||
vk::DynamicLoader dl;
|
||||
auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
auto vkGetInstanceProcAddr =
|
||||
dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||
|
||||
const vk::ApplicationInfo application_info = {
|
||||
.pApplicationName = "Citra",
|
||||
const vk::ApplicationInfo application_info = {.pApplicationName = "Citra",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.pEngineName = "Citra Vulkan",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.apiVersion = VK_API_VERSION_1_0
|
||||
};
|
||||
.apiVersion = VK_API_VERSION_1_0};
|
||||
|
||||
const vk::InstanceCreateInfo instance_info = {
|
||||
.pApplicationInfo = &application_info
|
||||
};
|
||||
const vk::InstanceCreateInfo instance_info = {.pApplicationInfo = &application_info};
|
||||
|
||||
instance = vk::createInstance(instance_info);
|
||||
|
||||
// Load required function pointers for querying the physical device
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumeratePhysicalDevices =
|
||||
PFN_vkEnumeratePhysicalDevices(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceProperties =
|
||||
PFN_vkGetPhysicalDeviceProperties(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumeratePhysicalDevices = PFN_vkEnumeratePhysicalDevices(
|
||||
vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceProperties = PFN_vkGetPhysicalDeviceProperties(
|
||||
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.vkDestroyInstance =
|
||||
PFN_vkDestroyInstance(vkGetInstanceProcAddr(instance, "vkDestroyInstance"));
|
||||
|
||||
@ -74,7 +71,8 @@ Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool
|
||||
|
||||
// Fetch instance independant function pointers
|
||||
vk::DynamicLoader dl;
|
||||
auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
auto vkGetInstanceProcAddr =
|
||||
dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||
|
||||
// Enable the instance extensions the backend uses
|
||||
@ -87,23 +85,20 @@ Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool
|
||||
return;
|
||||
}
|
||||
|
||||
const vk::ApplicationInfo application_info = {
|
||||
.pApplicationName = "Citra",
|
||||
const vk::ApplicationInfo application_info = {.pApplicationName = "Citra",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.pEngineName = "Citra Vulkan",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.apiVersion = available_version
|
||||
};
|
||||
.apiVersion = available_version};
|
||||
|
||||
const std::array layers = {"VK_LAYER_KHRONOS_validation"};
|
||||
const u32 layer_count = enable_validation ? 1u : 0u;
|
||||
const vk::InstanceCreateInfo instance_info = {
|
||||
.pApplicationInfo = &application_info,
|
||||
const vk::InstanceCreateInfo instance_info = {.pApplicationInfo = &application_info,
|
||||
.enabledLayerCount = layer_count,
|
||||
.ppEnabledLayerNames = layers.data(),
|
||||
.enabledExtensionCount = static_cast<u32>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data()
|
||||
};
|
||||
.enabledExtensionCount =
|
||||
static_cast<u32>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data()};
|
||||
|
||||
instance = vk::createInstance(instance_info);
|
||||
surface = CreateSurface(instance, window);
|
||||
@ -112,7 +107,8 @@ Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool
|
||||
physical_devices = instance.enumeratePhysicalDevices();
|
||||
if (const u16 physical_device_count = static_cast<u16>(physical_devices.size());
|
||||
physical_device_index >= physical_devices.size()) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Invalid physical device index {} provided when only {} devices exist",
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Invalid physical device index {} provided when only {} devices exist",
|
||||
physical_device_index, physical_device_count);
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -145,30 +141,20 @@ FormatTraits Instance::GetTraits(VideoCore::PixelFormat pixel_format) const {
|
||||
|
||||
void Instance::CreateFormatTable() {
|
||||
constexpr std::array pixel_formats = {
|
||||
VideoCore::PixelFormat::RGBA8,
|
||||
VideoCore::PixelFormat::RGB8,
|
||||
VideoCore::PixelFormat::RGB5A1,
|
||||
VideoCore::PixelFormat::RGB565,
|
||||
VideoCore::PixelFormat::RGBA4,
|
||||
VideoCore::PixelFormat::IA8,
|
||||
VideoCore::PixelFormat::RG8,
|
||||
VideoCore::PixelFormat::I8,
|
||||
VideoCore::PixelFormat::A8,
|
||||
VideoCore::PixelFormat::IA4,
|
||||
VideoCore::PixelFormat::I4,
|
||||
VideoCore::PixelFormat::A4,
|
||||
VideoCore::PixelFormat::ETC1,
|
||||
VideoCore::PixelFormat::ETC1A4,
|
||||
VideoCore::PixelFormat::D16,
|
||||
VideoCore::PixelFormat::D24,
|
||||
VideoCore::PixelFormat::D24S8
|
||||
};
|
||||
VideoCore::PixelFormat::RGBA8, VideoCore::PixelFormat::RGB8,
|
||||
VideoCore::PixelFormat::RGB5A1, VideoCore::PixelFormat::RGB565,
|
||||
VideoCore::PixelFormat::RGBA4, VideoCore::PixelFormat::IA8,
|
||||
VideoCore::PixelFormat::RG8, VideoCore::PixelFormat::I8,
|
||||
VideoCore::PixelFormat::A8, VideoCore::PixelFormat::IA4,
|
||||
VideoCore::PixelFormat::I4, VideoCore::PixelFormat::A4,
|
||||
VideoCore::PixelFormat::ETC1, VideoCore::PixelFormat::ETC1A4,
|
||||
VideoCore::PixelFormat::D16, VideoCore::PixelFormat::D24,
|
||||
VideoCore::PixelFormat::D24S8};
|
||||
|
||||
const vk::FormatFeatureFlags storage_usage = vk::FormatFeatureFlagBits::eStorageImage;
|
||||
const vk::FormatFeatureFlags blit_usage = vk::FormatFeatureFlagBits::eSampledImage |
|
||||
vk::FormatFeatureFlagBits::eTransferDst |
|
||||
vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
const vk::FormatFeatureFlags blit_usage =
|
||||
vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eTransferDst |
|
||||
vk::FormatFeatureFlagBits::eTransferSrc | vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitDst;
|
||||
|
||||
for (const auto& pixel_format : pixel_formats) {
|
||||
@ -176,12 +162,12 @@ void Instance::CreateFormatTable() {
|
||||
const vk::FormatProperties properties = physical_device.getFormatProperties(format);
|
||||
const vk::ImageAspectFlags aspect = GetImageAspect(format);
|
||||
|
||||
const vk::FormatFeatureFlagBits attachment_usage = (aspect & vk::ImageAspectFlagBits::eDepth) ?
|
||||
vk::FormatFeatureFlagBits::eDepthStencilAttachment :
|
||||
vk::FormatFeatureFlagBits::eColorAttachment;
|
||||
const vk::FormatFeatureFlagBits attachment_usage =
|
||||
(aspect & vk::ImageAspectFlagBits::eDepth)
|
||||
? vk::FormatFeatureFlagBits::eDepthStencilAttachment
|
||||
: vk::FormatFeatureFlagBits::eColorAttachment;
|
||||
|
||||
const bool supports_blit =
|
||||
(properties.optimalTilingFeatures & blit_usage) == blit_usage;
|
||||
const bool supports_blit = (properties.optimalTilingFeatures & blit_usage) == blit_usage;
|
||||
const bool supports_attachment =
|
||||
(properties.optimalTilingFeatures & attachment_usage) == attachment_usage;
|
||||
const bool supports_storage =
|
||||
@ -190,14 +176,13 @@ void Instance::CreateFormatTable() {
|
||||
// Find the most inclusive usage flags for this format
|
||||
vk::ImageUsageFlags best_usage;
|
||||
if (supports_blit) {
|
||||
best_usage |= vk::ImageUsageFlagBits::eSampled |
|
||||
vk::ImageUsageFlagBits::eTransferDst |
|
||||
best_usage |= vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eTransferSrc;
|
||||
}
|
||||
if (supports_attachment) {
|
||||
best_usage |= (aspect & vk::ImageAspectFlagBits::eDepth) ?
|
||||
vk::ImageUsageFlagBits::eDepthStencilAttachment :
|
||||
vk::ImageUsageFlagBits::eColorAttachment;
|
||||
best_usage |= (aspect & vk::ImageAspectFlagBits::eDepth)
|
||||
? vk::ImageUsageFlagBits::eDepthStencilAttachment
|
||||
: vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
if (supports_storage) {
|
||||
best_usage |= vk::ImageUsageFlagBits::eStorage;
|
||||
@ -219,26 +204,26 @@ void Instance::CreateFormatTable() {
|
||||
}
|
||||
|
||||
const u32 index = static_cast<u32>(pixel_format);
|
||||
format_table[index] = FormatTraits{
|
||||
.blit_support = supports_blit,
|
||||
format_table[index] = FormatTraits{.blit_support = supports_blit,
|
||||
.attachment_support = supports_attachment,
|
||||
.storage_support = supports_storage,
|
||||
.usage = best_usage,
|
||||
.native = format,
|
||||
.fallback = fallback
|
||||
};
|
||||
.fallback = fallback};
|
||||
}
|
||||
}
|
||||
|
||||
bool Instance::CreateDevice() {
|
||||
auto feature_chain = physical_device.getFeatures2<vk::PhysicalDeviceFeatures2,
|
||||
auto feature_chain =
|
||||
physical_device.getFeatures2<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT,
|
||||
vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR>();
|
||||
|
||||
// Not having geometry shaders will cause issues with accelerated rendering.
|
||||
const vk::PhysicalDeviceFeatures available = feature_chain.get().features;
|
||||
if (!available.geometryShader) {
|
||||
LOG_WARNING(Render_Vulkan, "Geometry shaders not availabe! Accelerated rendering not possible!");
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Geometry shaders not availabe! Accelerated rendering not possible!");
|
||||
}
|
||||
|
||||
auto extension_list = physical_device.enumerateDeviceExtensionProperties();
|
||||
@ -252,9 +237,9 @@ bool Instance::CreateDevice() {
|
||||
u32 enabled_extension_count = 0;
|
||||
|
||||
auto AddExtension = [&](std::string_view name) -> bool {
|
||||
auto result = std::find_if(extension_list.begin(), extension_list.end(), [&](const auto& prop) {
|
||||
return name.compare(prop.extensionName.data());
|
||||
});
|
||||
auto result =
|
||||
std::find_if(extension_list.begin(), extension_list.end(),
|
||||
[&](const auto& prop) { return name.compare(prop.extensionName.data()); });
|
||||
|
||||
if (result != extension_list.end()) {
|
||||
LOG_INFO(Render_Vulkan, "Enabling extension: {}", name);
|
||||
@ -311,17 +296,12 @@ bool Instance::CreateDevice() {
|
||||
static constexpr float queue_priorities[] = {1.0f};
|
||||
|
||||
const std::array queue_infos = {
|
||||
vk::DeviceQueueCreateInfo{
|
||||
.queueFamilyIndex = graphics_queue_family_index,
|
||||
vk::DeviceQueueCreateInfo{.queueFamilyIndex = graphics_queue_family_index,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = queue_priorities
|
||||
},
|
||||
vk::DeviceQueueCreateInfo{
|
||||
.queueFamilyIndex = present_queue_family_index,
|
||||
.pQueuePriorities = queue_priorities},
|
||||
vk::DeviceQueueCreateInfo{.queueFamilyIndex = present_queue_family_index,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = queue_priorities
|
||||
}
|
||||
};
|
||||
.pQueuePriorities = queue_priorities}};
|
||||
|
||||
const u32 queue_count = graphics_queue_family_index != present_queue_family_index ? 2u : 1u;
|
||||
const vk::StructureChain device_chain = {
|
||||
@ -332,8 +312,7 @@ bool Instance::CreateDevice() {
|
||||
.ppEnabledExtensionNames = enabled_extensions.data(),
|
||||
},
|
||||
vk::PhysicalDeviceFeatures2{
|
||||
.features = {
|
||||
.robustBufferAccess = available.robustBufferAccess,
|
||||
.features = {.robustBufferAccess = available.robustBufferAccess,
|
||||
.geometryShader = available.geometryShader,
|
||||
.dualSrcBlend = available.dualSrcBlend,
|
||||
.logicOp = available.logicOp,
|
||||
@ -342,15 +321,10 @@ bool Instance::CreateDevice() {
|
||||
.samplerAnisotropy = available.samplerAnisotropy,
|
||||
.fragmentStoresAndAtomics = available.fragmentStoresAndAtomics,
|
||||
.shaderStorageImageMultisample = available.shaderStorageImageMultisample,
|
||||
.shaderClipDistance = available.shaderClipDistance
|
||||
}
|
||||
},
|
||||
vk::PhysicalDeviceDepthClipControlFeaturesEXT{
|
||||
.depthClipControl = true
|
||||
},
|
||||
.shaderClipDistance = available.shaderClipDistance}},
|
||||
vk::PhysicalDeviceDepthClipControlFeaturesEXT{.depthClipControl = true},
|
||||
feature_chain.get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
|
||||
feature_chain.get<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR>()
|
||||
};
|
||||
feature_chain.get<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR>()};
|
||||
|
||||
// Create logical device
|
||||
device = physical_device.createDevice(device_chain.get());
|
||||
@ -367,16 +341,13 @@ bool Instance::CreateDevice() {
|
||||
void Instance::CreateAllocator() {
|
||||
const VmaVulkanFunctions functions = {
|
||||
.vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr,
|
||||
.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr
|
||||
};
|
||||
.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr};
|
||||
|
||||
const VmaAllocatorCreateInfo allocator_info = {
|
||||
.physicalDevice = physical_device,
|
||||
const VmaAllocatorCreateInfo allocator_info = {.physicalDevice = physical_device,
|
||||
.device = device,
|
||||
.pVulkanFunctions = &functions,
|
||||
.instance = instance,
|
||||
.vulkanApiVersion = VK_API_VERSION_1_1
|
||||
};
|
||||
.vulkanApiVersion = VK_API_VERSION_1_1};
|
||||
|
||||
if (VkResult result = vmaCreateAllocator(&allocator_info, &allocator); result != VK_SUCCESS) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Failed to initialize VMA with error {}", result);
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/pica_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
|
||||
@ -22,51 +22,26 @@ struct Bindings {
|
||||
|
||||
constexpr u32 RASTERIZER_SET_COUNT = 4;
|
||||
constexpr static std::array RASTERIZER_SETS = {
|
||||
Bindings{
|
||||
// Utility set
|
||||
.bindings = {
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
Bindings{// Utility set
|
||||
.bindings = {vk::DescriptorType::eUniformBuffer, vk::DescriptorType::eUniformBuffer,
|
||||
vk::DescriptorType::eUniformTexelBuffer,
|
||||
vk::DescriptorType::eUniformTexelBuffer,
|
||||
vk::DescriptorType::eUniformTexelBuffer
|
||||
},
|
||||
.binding_count = 5
|
||||
},
|
||||
Bindings{
|
||||
// Texture set
|
||||
.bindings = {
|
||||
vk::DescriptorType::eSampledImage,
|
||||
vk::DescriptorType::eSampledImage,
|
||||
vk::DescriptorType::eSampledImage,
|
||||
vk::DescriptorType::eSampledImage
|
||||
},
|
||||
.binding_count = 4
|
||||
},
|
||||
Bindings{
|
||||
// Sampler set
|
||||
.bindings = {
|
||||
vk::DescriptorType::eSampler,
|
||||
vk::DescriptorType::eSampler,
|
||||
vk::DescriptorType::eSampler,
|
||||
vk::DescriptorType::eSampler
|
||||
},
|
||||
.binding_count = 4
|
||||
},
|
||||
Bindings {
|
||||
// Shadow set
|
||||
.bindings = {
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage
|
||||
},
|
||||
.binding_count = 7
|
||||
}
|
||||
};
|
||||
vk::DescriptorType::eUniformTexelBuffer},
|
||||
.binding_count = 5},
|
||||
Bindings{// Texture set
|
||||
.bindings = {vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage,
|
||||
vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage},
|
||||
.binding_count = 4},
|
||||
Bindings{// Sampler set
|
||||
.bindings = {vk::DescriptorType::eSampler, vk::DescriptorType::eSampler,
|
||||
vk::DescriptorType::eSampler, vk::DescriptorType::eSampler},
|
||||
.binding_count = 4},
|
||||
Bindings{// Shadow set
|
||||
.bindings = {vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
|
||||
vk::DescriptorType::eStorageImage},
|
||||
.binding_count = 7}};
|
||||
|
||||
constexpr vk::ShaderStageFlags ToVkStageFlags(vk::DescriptorType type) {
|
||||
vk::ShaderStageFlags flags;
|
||||
@ -79,10 +54,8 @@ constexpr vk::ShaderStageFlags ToVkStageFlags(vk::DescriptorType type) {
|
||||
break;
|
||||
case vk::DescriptorType::eUniformBuffer:
|
||||
case vk::DescriptorType::eUniformBufferDynamic:
|
||||
flags = vk::ShaderStageFlagBits::eFragment |
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eGeometry |
|
||||
vk::ShaderStageFlagBits::eCompute;
|
||||
flags = vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eCompute;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Render_Vulkan, "Unknown descriptor type!");
|
||||
@ -109,10 +82,14 @@ vk::Format ToVkAttributeFormat(VertexAttribute attrib) {
|
||||
switch (attrib.type) {
|
||||
case AttribType::Float:
|
||||
switch (attrib.size) {
|
||||
case 1: return vk::Format::eR32Sfloat;
|
||||
case 2: return vk::Format::eR32G32Sfloat;
|
||||
case 3: return vk::Format::eR32G32B32Sfloat;
|
||||
case 4: return vk::Format::eR32G32B32A32Sfloat;
|
||||
case 1:
|
||||
return vk::Format::eR32Sfloat;
|
||||
case 2:
|
||||
return vk::Format::eR32G32Sfloat;
|
||||
case 3:
|
||||
return vk::Format::eR32G32B32Sfloat;
|
||||
case 4:
|
||||
return vk::Format::eR32G32B32A32Sfloat;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Render_Vulkan, "Unimplemented vertex attribute format!");
|
||||
@ -124,9 +101,12 @@ vk::Format ToVkAttributeFormat(VertexAttribute attrib) {
|
||||
|
||||
vk::ShaderStageFlagBits ToVkShaderStage(std::size_t index) {
|
||||
switch (index) {
|
||||
case 0: return vk::ShaderStageFlagBits::eVertex;
|
||||
case 1: return vk::ShaderStageFlagBits::eFragment;
|
||||
case 2: return vk::ShaderStageFlagBits::eGeometry;
|
||||
case 0:
|
||||
return vk::ShaderStageFlagBits::eVertex;
|
||||
case 1:
|
||||
return vk::ShaderStageFlagBits::eFragment;
|
||||
case 2:
|
||||
return vk::ShaderStageFlagBits::eGeometry;
|
||||
default:
|
||||
LOG_CRITICAL(Render_Vulkan, "Invalid shader stage index!");
|
||||
UNREACHABLE();
|
||||
@ -135,7 +115,8 @@ vk::ShaderStageFlagBits ToVkShaderStage(std::size_t index) {
|
||||
return vk::ShaderStageFlagBits::eVertex;
|
||||
}
|
||||
|
||||
PipelineCache::PipelineCache(const Instance& instance, TaskScheduler& scheduler, RenderpassCache& renderpass_cache)
|
||||
PipelineCache::PipelineCache(const Instance& instance, TaskScheduler& scheduler,
|
||||
RenderpassCache& renderpass_cache)
|
||||
: instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache} {
|
||||
descriptor_dirty.fill(true);
|
||||
|
||||
@ -185,9 +166,10 @@ void PipelineCache::BindPipeline(const PipelineInfo& info) {
|
||||
shader_hash = Common::HashCombine(shader_hash, shader_hashes[i]);
|
||||
}
|
||||
|
||||
const u64 info_hash_size = instance.IsExtendedDynamicStateSupported() ?
|
||||
offsetof(PipelineInfo, rasterization) :
|
||||
offsetof(PipelineInfo, depth_stencil) + offsetof(DepthStencilState, stencil_reference);
|
||||
const u64 info_hash_size = instance.IsExtendedDynamicStateSupported()
|
||||
? offsetof(PipelineInfo, rasterization)
|
||||
: offsetof(PipelineInfo, depth_stencil) +
|
||||
offsetof(DepthStencilState, stencil_reference);
|
||||
|
||||
u64 info_hash = Common::ComputeHash64(&info, info_hash_size);
|
||||
u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash);
|
||||
@ -206,9 +188,11 @@ void PipelineCache::BindPipeline(const PipelineInfo& info) {
|
||||
BindDescriptorSets();
|
||||
}
|
||||
|
||||
bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup) {
|
||||
bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
|
||||
Pica::Shader::ShaderSetup& setup) {
|
||||
const PicaVSConfig config{regs.vs, setup};
|
||||
auto [handle, result] = programmable_vertex_shaders.Get(config, setup, vk::ShaderStageFlagBits::eVertex,
|
||||
auto [handle, result] =
|
||||
programmable_vertex_shaders.Get(config, setup, vk::ShaderStageFlagBits::eVertex,
|
||||
instance.GetDevice(), ShaderOptimization::Debug);
|
||||
if (!handle) {
|
||||
return false;
|
||||
@ -247,48 +231,33 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
||||
|
||||
void PipelineCache::BindTexture(u32 binding, vk::ImageView image_view) {
|
||||
const vk::DescriptorImageInfo image_info = {
|
||||
.imageView = image_view,
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
|
||||
};
|
||||
.imageView = image_view, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
|
||||
SetBinding(1, binding, DescriptorData{image_info});
|
||||
}
|
||||
|
||||
void PipelineCache::BindStorageImage(u32 binding, vk::ImageView image_view) {
|
||||
const vk::DescriptorImageInfo image_info = {
|
||||
.imageView = image_view,
|
||||
.imageLayout = vk::ImageLayout::eGeneral
|
||||
};
|
||||
const vk::DescriptorImageInfo image_info = {.imageView = image_view,
|
||||
.imageLayout = vk::ImageLayout::eGeneral};
|
||||
|
||||
SetBinding(3, binding, DescriptorData{image_info});
|
||||
}
|
||||
|
||||
void PipelineCache::BindBuffer(u32 binding, vk::Buffer buffer, u32 offset, u32 size) {
|
||||
const DescriptorData data = {
|
||||
.buffer_info = vk::DescriptorBufferInfo{
|
||||
.buffer = buffer,
|
||||
.offset = offset,
|
||||
.range = size
|
||||
}
|
||||
};
|
||||
.buffer_info = vk::DescriptorBufferInfo{.buffer = buffer, .offset = offset, .range = size}};
|
||||
|
||||
SetBinding(0, binding, data);
|
||||
}
|
||||
|
||||
void PipelineCache::BindTexelBuffer(u32 binding, vk::BufferView buffer_view) {
|
||||
const DescriptorData data = {
|
||||
.buffer_view = buffer_view
|
||||
};
|
||||
const DescriptorData data = {.buffer_view = buffer_view};
|
||||
|
||||
SetBinding(0, binding, data);
|
||||
}
|
||||
|
||||
void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) {
|
||||
const DescriptorData data = {
|
||||
.image_info = vk::DescriptorImageInfo{
|
||||
.sampler = sampler
|
||||
}
|
||||
};
|
||||
const DescriptorData data = {.image_info = vk::DescriptorImageInfo{.sampler = sampler}};
|
||||
|
||||
SetBinding(2, binding, data);
|
||||
}
|
||||
@ -311,7 +280,8 @@ void PipelineCache::MarkDirty() {
|
||||
void PipelineCache::ApplyDynamic(const PipelineInfo& info) {
|
||||
if (instance.IsExtendedDynamicStateSupported()) {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.setPrimitiveTopologyEXT(PicaToVK::PrimitiveTopology(info.rasterization.topology));
|
||||
command_buffer.setPrimitiveTopologyEXT(
|
||||
PicaToVK::PrimitiveTopology(info.rasterization.topology));
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,27 +301,22 @@ void PipelineCache::BuildLayout() {
|
||||
const auto& set = RASTERIZER_SETS[i];
|
||||
for (u32 j = 0; j < set.binding_count; j++) {
|
||||
vk::DescriptorType type = set.bindings[j];
|
||||
set_bindings[j] = vk::DescriptorSetLayoutBinding{
|
||||
.binding = j,
|
||||
set_bindings[j] = vk::DescriptorSetLayoutBinding{.binding = j,
|
||||
.descriptorType = type,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = ToVkStageFlags(type)
|
||||
};
|
||||
.stageFlags = ToVkStageFlags(type)};
|
||||
|
||||
update_entries[j] = vk::DescriptorUpdateTemplateEntry{
|
||||
.dstBinding = j,
|
||||
update_entries[j] =
|
||||
vk::DescriptorUpdateTemplateEntry{.dstBinding = j,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.offset = j * sizeof(DescriptorData),
|
||||
.stride = 0
|
||||
};
|
||||
.stride = 0};
|
||||
}
|
||||
|
||||
const vk::DescriptorSetLayoutCreateInfo layout_info = {
|
||||
.bindingCount = set.binding_count,
|
||||
.pBindings = set_bindings.data()
|
||||
};
|
||||
const vk::DescriptorSetLayoutCreateInfo layout_info = {.bindingCount = set.binding_count,
|
||||
.pBindings = set_bindings.data()};
|
||||
|
||||
// Create descriptor set layout
|
||||
descriptor_set_layouts[i] = device.createDescriptorSetLayout(layout_info);
|
||||
@ -360,19 +325,16 @@ void PipelineCache::BuildLayout() {
|
||||
.descriptorUpdateEntryCount = set.binding_count,
|
||||
.pDescriptorUpdateEntries = update_entries.data(),
|
||||
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
|
||||
.descriptorSetLayout = descriptor_set_layouts[i]
|
||||
};
|
||||
.descriptorSetLayout = descriptor_set_layouts[i]};
|
||||
|
||||
// Create descriptor set update template
|
||||
update_templates[i] = device.createDescriptorUpdateTemplate(template_info);
|
||||
}
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = RASTERIZER_SET_COUNT,
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {.setLayoutCount = RASTERIZER_SET_COUNT,
|
||||
.pSetLayouts = descriptor_set_layouts.data(),
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr
|
||||
};
|
||||
.pPushConstantRanges = nullptr};
|
||||
|
||||
layout = device.createPipelineLayout(layout_info);
|
||||
}
|
||||
@ -389,16 +351,14 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
}
|
||||
|
||||
shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{
|
||||
.stage = ToVkShaderStage(i),
|
||||
.module = shader,
|
||||
.pName = "main"
|
||||
};
|
||||
.stage = ToVkShaderStage(i), .module = shader, .pName = "main"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Vulkan doesn't intuitively support fixed attributes. To avoid duplicating the data and increasing
|
||||
* data upload, when the fixed flag is true, we specify VK_VERTEX_INPUT_RATE_INSTANCE as the input rate.
|
||||
* Since one instance is all we render, the shader will always read the single attribute.
|
||||
* Vulkan doesn't intuitively support fixed attributes. To avoid duplicating the data and
|
||||
* increasing data upload, when the fixed flag is true, we specify VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
* as the input rate. Since one instance is all we render, the shader will always read the
|
||||
* single attribute.
|
||||
*/
|
||||
std::array<vk::VertexInputBindingDescription, MAX_VERTEX_BINDINGS> bindings;
|
||||
for (u32 i = 0; i < info.vertex_layout.binding_count; i++) {
|
||||
@ -407,33 +367,28 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
.binding = binding.binding,
|
||||
.stride = binding.stride,
|
||||
.inputRate = binding.fixed.Value() ? vk::VertexInputRate::eInstance
|
||||
: vk::VertexInputRate::eVertex
|
||||
};
|
||||
: vk::VertexInputRate::eVertex};
|
||||
}
|
||||
|
||||
// Populate vertex attribute structures
|
||||
std::array<vk::VertexInputAttributeDescription, MAX_VERTEX_ATTRIBUTES> attributes;
|
||||
for (u32 i = 0; i < info.vertex_layout.attribute_count; i++) {
|
||||
const auto& attr = info.vertex_layout.attributes[i];
|
||||
attributes[i] = vk::VertexInputAttributeDescription{
|
||||
.location = attr.location,
|
||||
attributes[i] = vk::VertexInputAttributeDescription{.location = attr.location,
|
||||
.binding = attr.binding,
|
||||
.format = ToVkAttributeFormat(attr),
|
||||
.offset = attr.offset
|
||||
};
|
||||
.offset = attr.offset};
|
||||
}
|
||||
|
||||
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
|
||||
.vertexBindingDescriptionCount = info.vertex_layout.binding_count,
|
||||
.pVertexBindingDescriptions = bindings.data(),
|
||||
.vertexAttributeDescriptionCount = info.vertex_layout.attribute_count,
|
||||
.pVertexAttributeDescriptions = attributes.data()
|
||||
};
|
||||
.pVertexAttributeDescriptions = attributes.data()};
|
||||
|
||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
||||
.topology = PicaToVK::PrimitiveTopology(info.rasterization.topology),
|
||||
.primitiveRestartEnable = false
|
||||
};
|
||||
.primitiveRestartEnable = false};
|
||||
|
||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
||||
.depthClampEnable = false,
|
||||
@ -441,13 +396,10 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
.cullMode = PicaToVK::CullMode(info.rasterization.cull_mode),
|
||||
.frontFace = PicaToVK::FrontFace(info.rasterization.cull_mode),
|
||||
.depthBiasEnable = false,
|
||||
.lineWidth = 1.0f
|
||||
};
|
||||
.lineWidth = 1.0f};
|
||||
|
||||
const vk::PipelineMultisampleStateCreateInfo multisampling = {
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||
.sampleShadingEnable = false
|
||||
};
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = false};
|
||||
|
||||
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
|
||||
.blendEnable = info.blending.blend_enable.Value(),
|
||||
@ -458,34 +410,22 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
.dstAlphaBlendFactor = PicaToVK::BlendFunc(info.blending.dst_alpha_blend_factor),
|
||||
.alphaBlendOp = PicaToVK::BlendEquation(info.blending.alpha_blend_eq),
|
||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
};
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
|
||||
|
||||
const vk::PipelineColorBlendStateCreateInfo color_blending = {
|
||||
.logicOpEnable = info.blending.logic_op_enable.Value(),
|
||||
.logicOp = PicaToVK::LogicOp(info.blending.logic_op),
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorblend_attachment,
|
||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}
|
||||
};
|
||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
|
||||
const vk::Viewport viewport = {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = 1.0f,
|
||||
.height = 1.0f,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
};
|
||||
.x = 0.0f, .y = 0.0f, .width = 1.0f, .height = 1.0f, .minDepth = 0.0f, .maxDepth = 1.0f};
|
||||
|
||||
const vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = {1, 1}
|
||||
};
|
||||
const vk::Rect2D scissor = {.offset = {0, 0}, .extent = {1, 1}};
|
||||
|
||||
vk::PipelineViewportDepthClipControlCreateInfoEXT depth_clip_control = {
|
||||
.negativeOneToOne = true
|
||||
};
|
||||
vk::PipelineViewportDepthClipControlCreateInfoEXT depth_clip_control = {.negativeOneToOne =
|
||||
true};
|
||||
|
||||
const vk::PipelineViewportStateCreateInfo viewport_info = {
|
||||
.pNext = &depth_clip_control,
|
||||
@ -515,17 +455,14 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
};
|
||||
|
||||
const vk::PipelineDynamicStateCreateInfo dynamic_info = {
|
||||
.dynamicStateCount =
|
||||
extended_dynamic_states ? static_cast<u32>(dynamic_states.size()) : 6u,
|
||||
.pDynamicStates = dynamic_states.data()
|
||||
};
|
||||
.dynamicStateCount = extended_dynamic_states ? static_cast<u32>(dynamic_states.size()) : 6u,
|
||||
.pDynamicStates = dynamic_states.data()};
|
||||
|
||||
const vk::StencilOpState stencil_op_state = {
|
||||
.failOp = PicaToVK::StencilOp(info.depth_stencil.stencil_fail_op),
|
||||
.passOp = PicaToVK::StencilOp(info.depth_stencil.stencil_pass_op),
|
||||
.depthFailOp = PicaToVK::StencilOp(info.depth_stencil.stencil_depth_fail_op),
|
||||
.compareOp = PicaToVK::CompareFunc(info.depth_stencil.stencil_compare_op)
|
||||
};
|
||||
.compareOp = PicaToVK::CompareFunc(info.depth_stencil.stencil_compare_op)};
|
||||
|
||||
const vk::PipelineDepthStencilStateCreateInfo depth_info = {
|
||||
.depthTestEnable = static_cast<u32>(info.depth_stencil.depth_test_enable.Value()),
|
||||
@ -534,8 +471,7 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
.depthBoundsTestEnable = false,
|
||||
.stencilTestEnable = static_cast<u32>(info.depth_stencil.stencil_test_enable.Value()),
|
||||
.front = stencil_op_state,
|
||||
.back = stencil_op_state
|
||||
};
|
||||
.back = stencil_op_state};
|
||||
|
||||
const vk::GraphicsPipelineCreateInfo pipeline_info = {
|
||||
.stageCount = shader_count,
|
||||
@ -549,9 +485,8 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
|
||||
.pColorBlendState = &color_blending,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.layout = layout,
|
||||
.renderPass = renderpass_cache.GetRenderpass(info.color_attachment,
|
||||
info.depth_attachment, false)
|
||||
};
|
||||
.renderPass =
|
||||
renderpass_cache.GetRenderpass(info.color_attachment, info.depth_attachment, false)};
|
||||
|
||||
if (const auto result = device.createGraphicsPipeline(pipeline_cache, pipeline_info);
|
||||
result.result == vk::Result::eSuccess) {
|
||||
@ -573,8 +508,7 @@ void PipelineCache::BindDescriptorSets() {
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {
|
||||
.descriptorPool = scheduler.GetDescriptorPool(),
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &descriptor_set_layouts[i]
|
||||
};
|
||||
.pSetLayouts = &descriptor_set_layouts[i]};
|
||||
|
||||
vk::DescriptorSet set = device.allocateDescriptorSets(alloc_info)[0];
|
||||
device.updateDescriptorSetWithTemplate(set, update_templates[i], update_data[i][0]);
|
||||
@ -586,8 +520,8 @@ void PipelineCache::BindDescriptorSets() {
|
||||
|
||||
// Bind the descriptor sets
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, RASTERIZER_SET_COUNT,
|
||||
descriptor_sets.data(), 0, nullptr);
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0,
|
||||
RASTERIZER_SET_COUNT, descriptor_sets.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
void PipelineCache::LoadDiskCache() {
|
||||
@ -596,10 +530,7 @@ void PipelineCache::LoadDiskCache() {
|
||||
}
|
||||
|
||||
const std::string cache_file_path = GetPipelineCacheDir() + DIR_SEP "pipelines.bin";
|
||||
vk::PipelineCacheCreateInfo cache_info = {
|
||||
.initialDataSize = 0,
|
||||
.pInitialData = nullptr
|
||||
};
|
||||
vk::PipelineCacheCreateInfo cache_info = {.initialDataSize = 0, .pInitialData = nullptr};
|
||||
|
||||
FileUtil::IOFile cache_file{cache_file_path, "r"};
|
||||
if (cache_file.IsOpen()) {
|
||||
@ -664,14 +595,16 @@ bool PipelineCache::IsCacheValid(const u8* data, u32 size) const {
|
||||
}
|
||||
|
||||
if (u32 vendor_id = instance.GetVendorID(); header.vendorID != vendor_id) {
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
LOG_ERROR(
|
||||
Render_Vulkan,
|
||||
"Pipeline cache failed validation: Incorrect vendor ID (file: {:#X}, device: {:#X})",
|
||||
header.vendorID, vendor_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (u32 device_id = instance.GetDeviceID(); header.deviceID != device_id) {
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
LOG_ERROR(
|
||||
Render_Vulkan,
|
||||
"Pipeline cache failed validation: Incorrect device ID (file: {:#X}, device: {:#X})",
|
||||
header.deviceID, device_id);
|
||||
return false;
|
||||
|
@ -8,11 +8,11 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/hash.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/regs.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
||||
#include "video_core/shader/shader_cache.h"
|
||||
#include "video_core/regs.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -22,13 +22,7 @@ constexpr u32 MAX_VERTEX_BINDINGS = 16;
|
||||
constexpr u32 MAX_DESCRIPTORS = 8;
|
||||
constexpr u32 MAX_DESCRIPTOR_SETS = 6;
|
||||
|
||||
enum class AttribType : u32 {
|
||||
Float = 0,
|
||||
Int = 1,
|
||||
Short = 2,
|
||||
Byte = 3,
|
||||
Ubyte = 4
|
||||
};
|
||||
enum class AttribType : u32 { Float = 0, Int = 1, Short = 2, Byte = 3, Ubyte = 4 };
|
||||
|
||||
/**
|
||||
* The pipeline state is tightly packed with bitfields to reduce
|
||||
@ -111,8 +105,8 @@ struct PipelineInfo {
|
||||
const bool has_stencil = depth_attachment == VideoCore::PixelFormat::D24S8;
|
||||
const bool depth_write =
|
||||
depth_stencil.depth_test_enable && depth_stencil.depth_write_enable;
|
||||
const bool stencil_write =
|
||||
has_stencil && depth_stencil.stencil_test_enable && depth_stencil.stencil_write_mask != 0;
|
||||
const bool stencil_write = has_stencil && depth_stencil.stencil_test_enable &&
|
||||
depth_stencil.stencil_write_mask != 0;
|
||||
|
||||
return depth_write || stencil_write;
|
||||
}
|
||||
@ -133,16 +127,15 @@ using DescriptorSetData = std::array<DescriptorData, MAX_DESCRIPTORS>;
|
||||
/**
|
||||
* Vulkan specialized PICA shader caches
|
||||
*/
|
||||
using ProgrammableVertexShaders =
|
||||
Pica::Shader::ShaderDoubleCache<PicaVSConfig, vk::ShaderModule, &Compile, &GenerateVertexShader>;
|
||||
using ProgrammableVertexShaders = Pica::Shader::ShaderDoubleCache<PicaVSConfig, vk::ShaderModule,
|
||||
&Compile, &GenerateVertexShader>;
|
||||
|
||||
using FixedGeometryShaders =
|
||||
Pica::Shader::ShaderCache<PicaFixedGSConfig, vk::ShaderModule, &Compile, &GenerateFixedGeometryShader>;
|
||||
using FixedGeometryShaders = Pica::Shader::ShaderCache<PicaFixedGSConfig, vk::ShaderModule,
|
||||
&Compile, &GenerateFixedGeometryShader>;
|
||||
|
||||
using FragmentShaders =
|
||||
Pica::Shader::ShaderCache<PicaFSConfig, vk::ShaderModule, &Compile, &GenerateFragmentShader>;
|
||||
|
||||
|
||||
class Instance;
|
||||
class TaskScheduler;
|
||||
class RenderpassCache;
|
||||
@ -153,7 +146,8 @@ class RenderpassCache;
|
||||
*/
|
||||
class PipelineCache {
|
||||
public:
|
||||
PipelineCache(const Instance& instance, TaskScheduler& scheduler, RenderpassCache& renderpass_cache);
|
||||
PipelineCache(const Instance& instance, TaskScheduler& scheduler,
|
||||
RenderpassCache& renderpass_cache);
|
||||
~PipelineCache();
|
||||
|
||||
/// Binds a pipeline using the provided information
|
||||
@ -250,11 +244,7 @@ private:
|
||||
std::array<vk::DescriptorSet, MAX_DESCRIPTOR_SETS> descriptor_sets;
|
||||
|
||||
// Bound shader modules
|
||||
enum ProgramType : u32 {
|
||||
VS = 0,
|
||||
GS = 2,
|
||||
FS = 1
|
||||
};
|
||||
enum ProgramType : u32 { VS = 0, GS = 2, FS = 1 };
|
||||
|
||||
std::array<vk::ShaderModule, MAX_SHADER_STAGES> current_shaders;
|
||||
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
||||
|
@ -3,16 +3,16 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Include the vulkan platform specific header
|
||||
#if defined(ANDROID) || defined (__ANDROID__)
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#elif defined(_WIN32)
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#elif defined(__APPLE__)
|
||||
#define VK_USE_PLATFORM_MACOS_MVK
|
||||
#define VK_USE_PLATFORM_METAL_EXT
|
||||
#define VK_USE_PLATFORM_MACOS_MVK
|
||||
#define VK_USE_PLATFORM_METAL_EXT
|
||||
#else
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#endif
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
@ -33,9 +33,7 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& e
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (window_info.type == Frontend::WindowSystemType::Windows) {
|
||||
const vk::Win32SurfaceCreateInfoKHR win32_ci = {
|
||||
.hinstance = nullptr,
|
||||
.hwnd = static_cast<HWND>(window_info.render_surface)
|
||||
};
|
||||
.hinstance = nullptr, .hwnd = static_cast<HWND>(window_info.render_surface)};
|
||||
|
||||
if (instance.createWin32SurfaceKHR(&win32_ci, nullptr, &surface) != vk::Result::eSuccess) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Failed to initialize Win32 surface");
|
||||
@ -46,8 +44,7 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& e
|
||||
if (window_info.type == Frontend::WindowSystemType::X11) {
|
||||
const vk::XlibSurfaceCreateInfoKHR xlib_ci = {
|
||||
.dpy = static_cast<Display*>(window_info.display_connection),
|
||||
.window = reinterpret_cast<Window>(window_info.render_surface)
|
||||
};
|
||||
.window = reinterpret_cast<Window>(window_info.render_surface)};
|
||||
|
||||
if (instance.createXlibSurfaceKHR(&xlib_ci, nullptr, &surface) != vk::Result::eSuccess) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface");
|
||||
@ -58,10 +55,10 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& e
|
||||
if (window_info.type == Frontend::WindowSystemType::Wayland) {
|
||||
const vk::WaylandSurfaceCreateInfoKHR wayland_ci = {
|
||||
.display = static_cast<wl_display*>(window_info.display_connection),
|
||||
.surface = static_cast<wl_surface*>(window_info.render_surface)
|
||||
};
|
||||
.surface = static_cast<wl_surface*>(window_info.render_surface)};
|
||||
|
||||
if (instance.createWaylandSurfaceKHR(&wayland_ci, nullptr, &surface) != vk::Result::eSuccess) {
|
||||
if (instance.createWaylandSurfaceKHR(&wayland_ci, nullptr, &surface) !=
|
||||
vk::Result::eSuccess) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface");
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -75,7 +72,8 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& e
|
||||
return surface;
|
||||
}
|
||||
|
||||
std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils) {
|
||||
std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window_type,
|
||||
bool enable_debug_utils) {
|
||||
const auto properties = vk::enumerateInstanceExtensionProperties();
|
||||
if (properties.empty()) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
|
||||
|
@ -11,11 +11,12 @@
|
||||
namespace Frontend {
|
||||
class EmuWindow;
|
||||
enum class WindowSystemType : u8;
|
||||
}
|
||||
} // namespace Frontend
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils);
|
||||
std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window_type,
|
||||
bool enable_debug_utils);
|
||||
|
||||
vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& emu_window);
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "video_core/regs_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/pica_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
@ -26,7 +26,8 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
|
||||
RasterizerVulkan::HardwareVertex::HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
|
||||
RasterizerVulkan::HardwareVertex::HardwareVertex(const Pica::Shader::OutputVertex& v,
|
||||
bool flip_quaternion) {
|
||||
position[0] = v.pos.x.ToFloat32();
|
||||
position[1] = v.pos.y.ToFloat32();
|
||||
position[2] = v.pos.z.ToFloat32();
|
||||
@ -98,31 +99,31 @@ constexpr u32 INDEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
constexpr u32 UNIFORM_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
constexpr u32 TEXTURE_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
|
||||
constexpr std::array TEXTURE_BUFFER_LF_FORMATS = {
|
||||
vk::Format::eR32G32Sfloat
|
||||
};
|
||||
constexpr std::array TEXTURE_BUFFER_LF_FORMATS = {vk::Format::eR32G32Sfloat};
|
||||
|
||||
constexpr std::array TEXTURE_BUFFER_FORMATS = {
|
||||
vk::Format::eR32G32Sfloat,
|
||||
vk::Format::eR32G32B32A32Sfloat
|
||||
};
|
||||
constexpr std::array TEXTURE_BUFFER_FORMATS = {vk::Format::eR32G32Sfloat,
|
||||
vk::Format::eR32G32B32A32Sfloat};
|
||||
|
||||
RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instance& instance,
|
||||
TaskScheduler& scheduler, TextureRuntime& runtime,
|
||||
RenderpassCache& renderpass_cache)
|
||||
: instance{instance}, scheduler{scheduler}, runtime{runtime}, renderpass_cache{renderpass_cache},
|
||||
res_cache{*this, runtime}, pipeline_cache{instance, scheduler, renderpass_cache},
|
||||
vertex_buffer{instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}},
|
||||
uniform_buffer{instance, scheduler, UNIFORM_BUFFER_SIZE, vk::BufferUsageFlagBits::eUniformBuffer, {}},
|
||||
index_buffer{instance, scheduler, INDEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eIndexBuffer, {}},
|
||||
texture_buffer{instance, scheduler, TEXTURE_BUFFER_SIZE, vk::BufferUsageFlagBits::eUniformTexelBuffer,
|
||||
TEXTURE_BUFFER_FORMATS},
|
||||
texture_lf_buffer{instance, scheduler, TEXTURE_BUFFER_SIZE, vk::BufferUsageFlagBits::eUniformTexelBuffer,
|
||||
TEXTURE_BUFFER_LF_FORMATS} {
|
||||
: instance{instance}, scheduler{scheduler}, runtime{runtime},
|
||||
renderpass_cache{renderpass_cache}, res_cache{*this, runtime},
|
||||
pipeline_cache{instance, scheduler, renderpass_cache},
|
||||
vertex_buffer{
|
||||
instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}},
|
||||
uniform_buffer{
|
||||
instance, scheduler, UNIFORM_BUFFER_SIZE, vk::BufferUsageFlagBits::eUniformBuffer, {}},
|
||||
index_buffer{
|
||||
instance, scheduler, INDEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eIndexBuffer, {}},
|
||||
texture_buffer{instance, scheduler, TEXTURE_BUFFER_SIZE,
|
||||
vk::BufferUsageFlagBits::eUniformTexelBuffer, TEXTURE_BUFFER_FORMATS},
|
||||
texture_lf_buffer{instance, scheduler, TEXTURE_BUFFER_SIZE,
|
||||
vk::BufferUsageFlagBits::eUniformTexelBuffer, TEXTURE_BUFFER_LF_FORMATS} {
|
||||
|
||||
// Create a 1x1 clear texture to use in the NULL case,
|
||||
default_texture = runtime.Allocate(1, 1, VideoCore::PixelFormat::RGBA8,
|
||||
VideoCore::TextureType::Texture2D);
|
||||
default_texture =
|
||||
runtime.Allocate(1, 1, VideoCore::PixelFormat::RGBA8, VideoCore::TextureType::Texture2D);
|
||||
runtime.Transition(scheduler.GetUploadCommandBuffer(), default_texture,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal, 0, 1);
|
||||
|
||||
@ -142,8 +143,7 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan
|
||||
.min_filter = Pica::TexturingRegs::TextureConfig::TextureFilter::Linear,
|
||||
.mip_filter = Pica::TexturingRegs::TextureConfig::TextureFilter::Linear,
|
||||
.wrap_s = Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder,
|
||||
.wrap_t = Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder
|
||||
};
|
||||
.wrap_t = Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder};
|
||||
|
||||
default_sampler = CreateSampler(default_sampler_info);
|
||||
|
||||
@ -194,7 +194,7 @@ RasterizerVulkan::~RasterizerVulkan() {
|
||||
|
||||
void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
//shader_program_manager->LoadDiskCache(stop_loading, callback);
|
||||
// shader_program_manager->LoadDiskCache(stop_loading, callback);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncEntireState() {
|
||||
@ -327,7 +327,8 @@ RasterizerVulkan::VertexArrayInfo RasterizerVulkan::AnalyzeVertexArray(bool is_i
|
||||
return {vertex_min, vertex_max, vs_input_size};
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_min, u32 vs_input_index_max) {
|
||||
void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_min,
|
||||
u32 vs_input_index_max) {
|
||||
auto [array_ptr, array_offset, _] = vertex_buffer.Map(vs_input_size, 4);
|
||||
|
||||
/**
|
||||
@ -358,10 +359,12 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi
|
||||
u32 attribute_index = loader.GetComponent(comp);
|
||||
if (attribute_index < 12) {
|
||||
if (u32 size = vertex_attributes.GetNumElements(attribute_index); size != 0) {
|
||||
offset = Common::AlignUp(offset, vertex_attributes.GetElementSizeInBytes(attribute_index));
|
||||
offset = Common::AlignUp(
|
||||
offset, vertex_attributes.GetElementSizeInBytes(attribute_index));
|
||||
|
||||
const u32 input_reg = regs.vs.GetRegisterForAttribute(attribute_index);
|
||||
const u32 attrib_format = static_cast<u32>(vertex_attributes.GetFormat(attribute_index));
|
||||
const u32 attrib_format =
|
||||
static_cast<u32>(vertex_attributes.GetFormat(attribute_index));
|
||||
const AttribType type = vs_attrib_types[attrib_format];
|
||||
|
||||
// Define the attribute
|
||||
@ -377,13 +380,15 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi
|
||||
}
|
||||
|
||||
} else {
|
||||
// Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings respectively
|
||||
// Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings
|
||||
// respectively
|
||||
offset = Common::AlignUp(offset, 4);
|
||||
offset += (attribute_index - 11) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
const PAddr data_addr = base_address + loader.data_offset + (vs_input_index_min * loader.byte_count);
|
||||
const PAddr data_addr =
|
||||
base_address + loader.data_offset + (vs_input_index_min * loader.byte_count);
|
||||
const u32 vertex_num = vs_input_index_max - vs_input_index_min + 1;
|
||||
const u32 data_size = loader.byte_count * vertex_num;
|
||||
|
||||
@ -409,12 +414,8 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi
|
||||
const u32 reg = regs.vs.GetRegisterForAttribute(i);
|
||||
if (!enable_attributes[reg]) {
|
||||
const auto& attr = Pica::g_state.input_default_attributes.attr[i];
|
||||
const std::array data = {
|
||||
attr.x.ToFloat32(),
|
||||
attr.y.ToFloat32(),
|
||||
attr.z.ToFloat32(),
|
||||
attr.w.ToFloat32()
|
||||
};
|
||||
const std::array data = {attr.x.ToFloat32(), attr.y.ToFloat32(), attr.z.ToFloat32(),
|
||||
attr.w.ToFloat32()};
|
||||
|
||||
// Copy the data to the end of the buffer
|
||||
const u32 data_size = sizeof(float) * static_cast<u32>(data.size());
|
||||
@ -448,7 +449,8 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi
|
||||
|
||||
// Bind the vertex buffers with all the bindings
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindVertexBuffers(0, layout.binding_count, buffers.data(), binding_offsets.data());
|
||||
command_buffer.bindVertexBuffers(0, layout.binding_count, buffers.data(),
|
||||
binding_offsets.data());
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::SetupVertexShader() {
|
||||
@ -596,8 +598,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
surfaces_rect.left, surfaces_rect.right)), // Right
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
|
||||
viewport_rect_unscaled.bottom * res_scale,
|
||||
surfaces_rect.bottom, surfaces_rect.top))
|
||||
};
|
||||
surfaces_rect.bottom, surfaces_rect.top))};
|
||||
|
||||
if (uniform_block_data.data.framebuffer_scale != res_scale) {
|
||||
uniform_block_data.data.framebuffer_scale = res_scale;
|
||||
@ -606,13 +607,17 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
|
||||
// sub-rect changes, the scissor bounds also need to be updated.
|
||||
int scissor_x1 = static_cast<int>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
int scissor_y1 = static_cast<int>(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
int scissor_x1 =
|
||||
static_cast<int>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
|
||||
int scissor_y1 =
|
||||
static_cast<int>(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale);
|
||||
|
||||
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
|
||||
// scaling or doing multisampling.
|
||||
int scissor_x2 = static_cast<int>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
int scissor_y2 = static_cast<int>(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
int scissor_x2 =
|
||||
static_cast<int>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
|
||||
int scissor_y2 =
|
||||
static_cast<int>(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
|
||||
|
||||
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
|
||||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
|
||||
@ -626,10 +631,11 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
|
||||
auto CheckBarrier = [this, &color_surface = color_surface](vk::ImageView image_view, u32 texture_index) {
|
||||
auto CheckBarrier = [this, &color_surface = color_surface](vk::ImageView image_view,
|
||||
u32 texture_index) {
|
||||
if (color_surface && color_surface->alloc.image_view == image_view) {
|
||||
//auto temp_tex = backend->CreateTexture(texture->GetInfo());
|
||||
//temp_tex->CopyFrom(texture);
|
||||
// auto temp_tex = backend->CreateTexture(texture->GetInfo());
|
||||
// temp_tex->CopyFrom(texture);
|
||||
pipeline_cache.BindTexture(texture_index, image_view);
|
||||
} else {
|
||||
pipeline_cache.BindTexture(texture_index, image_view);
|
||||
@ -651,15 +657,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
const auto BindSampler = [&](u32 binding, SamplerInfo& info,
|
||||
const Pica::TexturingRegs::TextureConfig& config) {
|
||||
// TODO(GPUCode): Cubemaps don't contain any mipmaps for now, so sampling from them returns nothing
|
||||
// Always sample from the base level until mipmaps for texture cubes are implemented
|
||||
// NOTE: There are no Vulkan filter modes that directly correspond to OpenGL minification filters
|
||||
// GL_LINEAR/GL_NEAREST so emulate them by setting
|
||||
// minLod = 0, and maxLod = 0.25, and using minFilter = VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST
|
||||
const bool skip_mipmap =
|
||||
config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
|
||||
info = SamplerInfo{
|
||||
.mag_filter = config.mag_filter,
|
||||
// TODO(GPUCode): Cubemaps don't contain any mipmaps for now, so sampling from them returns
|
||||
// nothing Always sample from the base level until mipmaps for texture cubes are implemented
|
||||
// NOTE: There are no Vulkan filter modes that directly correspond to OpenGL minification
|
||||
// filters GL_LINEAR/GL_NEAREST so emulate them by setting minLod = 0, and maxLod = 0.25,
|
||||
// and using minFilter = VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST
|
||||
const bool skip_mipmap = config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
|
||||
info =
|
||||
SamplerInfo{.mag_filter = config.mag_filter,
|
||||
.min_filter = config.min_filter,
|
||||
.mip_filter = config.mip_filter,
|
||||
.wrap_s = config.wrap_s,
|
||||
@ -667,8 +672,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
.border_color = config.border_color.raw,
|
||||
.lod_min = skip_mipmap ? 0.f : static_cast<float>(config.lod.min_level),
|
||||
.lod_max = skip_mipmap ? 0.25f : static_cast<float>(config.lod.max_level),
|
||||
.lod_bias = static_cast<float>(config.lod.bias)
|
||||
};
|
||||
.lod_bias = static_cast<float>(config.lod.bias)};
|
||||
|
||||
// Search the cache and bind the appropriate sampler
|
||||
if (auto it = samplers.find(info); it != samplers.end()) {
|
||||
@ -722,14 +726,13 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
|
||||
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
|
||||
.width = texture.config.width,
|
||||
.format = texture.format
|
||||
};
|
||||
.format = texture.format};
|
||||
|
||||
auto surface = res_cache.GetTextureCube(config);
|
||||
if (surface != nullptr) {
|
||||
runtime.Transition(scheduler.GetRenderCommandBuffer(), surface->alloc,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
0, surface->alloc.levels, 0, 6);
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal, 0,
|
||||
surface->alloc.levels, 0, 6);
|
||||
pipeline_cache.BindTexture(3, surface->alloc.image_view);
|
||||
} else {
|
||||
pipeline_cache.BindTexture(3, default_texture.image_view);
|
||||
@ -749,8 +752,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
runtime.Transition(scheduler.GetRenderCommandBuffer(), surface->alloc,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
0, surface->alloc.levels);
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal, 0,
|
||||
surface->alloc.levels);
|
||||
CheckBarrier(surface->alloc.image_view, texture_index);
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
@ -760,7 +763,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
// the geometry in question.
|
||||
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
|
||||
// on the male character's face, which in the OpenGL default appear black.
|
||||
//state.texture_units[texture_index].texture_2d = default_texture;
|
||||
// state.texture_units[texture_index].texture_2d = default_texture;
|
||||
pipeline_cache.BindTexture(texture_index, default_texture.image_view);
|
||||
}
|
||||
} else {
|
||||
@ -769,8 +772,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will cause
|
||||
// issues as the rasterizer cache might cause a scheduler switch and invalidate our state
|
||||
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will
|
||||
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
|
||||
|
||||
// Sync the viewport
|
||||
pipeline_cache.SetViewport(surfaces_rect.left + viewport_rect_unscaled.left * res_scale,
|
||||
@ -793,7 +796,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
|
||||
// Enable scissor test to prevent drawing outside of the framebuffer region
|
||||
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(), draw_rect.GetHeight());
|
||||
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(),
|
||||
draw_rect.GetHeight());
|
||||
|
||||
auto valid_surface = color_surface ? color_surface : depth_surface;
|
||||
const FramebufferInfo framebuffer_info = {
|
||||
@ -802,8 +806,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
.renderpass = renderpass_cache.GetRenderpass(pipeline_info.color_attachment,
|
||||
pipeline_info.depth_attachment, false),
|
||||
.width = valid_surface->GetScaledWidth(),
|
||||
.height = valid_surface->GetScaledHeight()
|
||||
};
|
||||
.height = valid_surface->GetScaledHeight()};
|
||||
|
||||
auto [it, new_framebuffer] = framebuffers.try_emplace(framebuffer_info, vk::Framebuffer{});
|
||||
if (new_framebuffer) {
|
||||
@ -813,29 +816,26 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
if (color_surface) {
|
||||
runtime.Transition(command_buffer, color_surface->alloc,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
0, color_surface->alloc.levels);
|
||||
vk::ImageLayout::eColorAttachmentOptimal, 0,
|
||||
color_surface->alloc.levels);
|
||||
}
|
||||
|
||||
if (depth_surface) {
|
||||
runtime.Transition(command_buffer, depth_surface->alloc,
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
0, depth_surface->alloc.levels);
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal, 0,
|
||||
depth_surface->alloc.levels);
|
||||
}
|
||||
|
||||
const vk::RenderPassBeginInfo renderpass_begin = {
|
||||
.renderPass =
|
||||
renderpass_cache.GetRenderpass(pipeline_info.color_attachment,
|
||||
.renderPass = renderpass_cache.GetRenderpass(pipeline_info.color_attachment,
|
||||
pipeline_info.depth_attachment, false),
|
||||
.framebuffer = it->second,
|
||||
.renderArea = vk::Rect2D{
|
||||
.offset = {static_cast<s32>(draw_rect.left), static_cast<s32>(draw_rect.bottom)},
|
||||
.extent = {draw_rect.GetWidth(), draw_rect.GetHeight()}
|
||||
},
|
||||
.renderArea = vk::Rect2D{.offset = {static_cast<s32>(draw_rect.left),
|
||||
static_cast<s32>(draw_rect.bottom)},
|
||||
.extent = {draw_rect.GetWidth(), draw_rect.GetHeight()}},
|
||||
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr
|
||||
};
|
||||
.pClearValues = nullptr};
|
||||
|
||||
renderpass_cache.EnterRenderpass(renderpass_begin);
|
||||
|
||||
@ -853,7 +853,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
// that when base_vertex is zero the GPU will start drawing from the current mapped
|
||||
// offset not the start of the buffer.
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer.GetHandle(), vertex_buffer.GetBufferOffset());
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer.GetHandle(),
|
||||
vertex_buffer.GetBufferOffset());
|
||||
|
||||
const u32 max_vertices = VERTEX_BUFFER_SIZE / sizeof(HardwareVertex);
|
||||
const u32 batch_size = static_cast<u32>(vertex_batch.size());
|
||||
@ -874,11 +875,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
// Mark framebuffer surfaces as dirty
|
||||
const VideoCore::Rect2D draw_rect_unscaled = {
|
||||
draw_rect.left / res_scale,
|
||||
draw_rect.top / res_scale,
|
||||
draw_rect.right / res_scale,
|
||||
draw_rect.bottom / res_scale
|
||||
};
|
||||
draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale,
|
||||
draw_rect.bottom / res_scale};
|
||||
|
||||
if (color_surface != nullptr && write_color_fb) {
|
||||
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
|
||||
@ -1590,8 +1588,7 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) {
|
||||
.minLod = info.lod_min,
|
||||
.maxLod = info.lod_max,
|
||||
.borderColor = vk::BorderColor::eIntOpaqueBlack,
|
||||
.unnormalizedCoordinates = false
|
||||
};
|
||||
.unnormalizedCoordinates = false};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
return device.createSampler(sampler_info);
|
||||
@ -1609,14 +1606,12 @@ vk::Framebuffer RasterizerVulkan::CreateFramebuffer(const FramebufferInfo& info)
|
||||
attachments[attachment_count++] = info.depth;
|
||||
}
|
||||
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {
|
||||
.renderPass = info.renderpass,
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {.renderPass = info.renderpass,
|
||||
.attachmentCount = attachment_count,
|
||||
.pAttachments = attachments.data(),
|
||||
.width = info.width,
|
||||
.height = info.height,
|
||||
.layers = 1
|
||||
};
|
||||
.layers = 1};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
return device.createFramebuffer(framebuffer_info);
|
||||
@ -1660,7 +1655,8 @@ void RasterizerVulkan::SyncCullMode() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncDepthScale() {
|
||||
float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_range).ToFloat32();
|
||||
float depth_scale =
|
||||
Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_range).ToFloat32();
|
||||
|
||||
if (depth_scale != uniform_block_data.data.depth_scale) {
|
||||
uniform_block_data.data.depth_scale = depth_scale;
|
||||
@ -1669,7 +1665,8 @@ void RasterizerVulkan::SyncDepthScale() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncDepthOffset() {
|
||||
float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_near_plane).ToFloat32();
|
||||
float depth_offset =
|
||||
Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_near_plane).ToFloat32();
|
||||
|
||||
if (depth_offset != uniform_block_data.data.depth_offset) {
|
||||
uniform_block_data.data.depth_offset = depth_offset;
|
||||
@ -1678,18 +1675,25 @@ void RasterizerVulkan::SyncDepthOffset() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncBlendEnabled() {
|
||||
pipeline_info.blending.blend_enable.Assign(Pica::g_state.regs.framebuffer.output_merger.alphablend_enable);
|
||||
pipeline_info.blending.blend_enable.Assign(
|
||||
Pica::g_state.regs.framebuffer.output_merger.alphablend_enable);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncBlendFuncs() {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
pipeline_info.blending.color_blend_eq.Assign(regs.framebuffer.output_merger.alpha_blending.blend_equation_rgb);
|
||||
pipeline_info.blending.alpha_blend_eq.Assign(regs.framebuffer.output_merger.alpha_blending.blend_equation_a);
|
||||
pipeline_info.blending.src_color_blend_factor.Assign(regs.framebuffer.output_merger.alpha_blending.factor_source_rgb);
|
||||
pipeline_info.blending.dst_color_blend_factor.Assign(regs.framebuffer.output_merger.alpha_blending.factor_dest_rgb);
|
||||
pipeline_info.blending.src_alpha_blend_factor.Assign(regs.framebuffer.output_merger.alpha_blending.factor_source_a);
|
||||
pipeline_info.blending.dst_alpha_blend_factor.Assign(regs.framebuffer.output_merger.alpha_blending.factor_dest_a);
|
||||
pipeline_info.blending.color_blend_eq.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.blend_equation_rgb);
|
||||
pipeline_info.blending.alpha_blend_eq.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.blend_equation_a);
|
||||
pipeline_info.blending.src_color_blend_factor.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.factor_source_rgb);
|
||||
pipeline_info.blending.dst_color_blend_factor.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.factor_dest_rgb);
|
||||
pipeline_info.blending.src_alpha_blend_factor.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.factor_source_a);
|
||||
pipeline_info.blending.dst_alpha_blend_factor.Assign(
|
||||
regs.framebuffer.output_merger.alpha_blending.factor_dest_a);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncBlendColor() {
|
||||
@ -1764,7 +1768,8 @@ void RasterizerVulkan::SyncStencilWriteMask() {
|
||||
: 0;
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, pipeline_info.depth_stencil.stencil_write_mask);
|
||||
command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
|
||||
pipeline_info.depth_stencil.stencil_write_mask);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncDepthWriteMask() {
|
||||
@ -1784,13 +1789,16 @@ void RasterizerVulkan::SyncDepthWriteMask() {
|
||||
void RasterizerVulkan::SyncStencilTest() {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
const bool test_enable = regs.framebuffer.output_merger.stencil_test.enable &&
|
||||
const bool test_enable =
|
||||
regs.framebuffer.output_merger.stencil_test.enable &&
|
||||
regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
|
||||
const auto& stencil_test = regs.framebuffer.output_merger.stencil_test;
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.input_mask);
|
||||
command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.reference_value);
|
||||
command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
|
||||
stencil_test.input_mask);
|
||||
command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
|
||||
stencil_test.reference_value);
|
||||
|
||||
if (instance.IsExtendedDynamicStateSupported()) {
|
||||
command_buffer.setStencilTestEnableEXT(test_enable);
|
||||
@ -1830,7 +1838,8 @@ void RasterizerVulkan::SyncDepthTest() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncCombinerColor() {
|
||||
auto combiner_color = PicaToVK::ColorRGBA8(Pica::g_state.regs.texturing.tev_combiner_buffer_color.raw);
|
||||
auto combiner_color =
|
||||
PicaToVK::ColorRGBA8(Pica::g_state.regs.texturing.tev_combiner_buffer_color.raw);
|
||||
if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
|
||||
uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
|
||||
uniform_block_data.dirty = true;
|
||||
@ -1893,8 +1902,7 @@ void RasterizerVulkan::SyncLightPosition(int light_index) {
|
||||
const Common::Vec3f position = {
|
||||
Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(),
|
||||
Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(),
|
||||
Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32()
|
||||
};
|
||||
Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32()};
|
||||
|
||||
if (position != uniform_block_data.data.light_src[light_index].position) {
|
||||
uniform_block_data.data.light_src[light_index].position = position;
|
||||
@ -1913,7 +1921,8 @@ void RasterizerVulkan::SyncLightSpotDirection(int light_index) {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncLightDistanceAttenuationBias(int light_index) {
|
||||
float dist_atten_bias = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias)
|
||||
float dist_atten_bias =
|
||||
Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias)
|
||||
.ToFloat32();
|
||||
|
||||
if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) {
|
||||
@ -1923,7 +1932,8 @@ void RasterizerVulkan::SyncLightDistanceAttenuationBias(int light_index) {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SyncLightDistanceAttenuationScale(int light_index) {
|
||||
float dist_atten_scale = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_scale)
|
||||
float dist_atten_scale =
|
||||
Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_scale)
|
||||
.ToFloat32();
|
||||
|
||||
if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) {
|
||||
@ -2032,8 +2042,9 @@ void RasterizerVulkan::SyncAndUploadLUTs() {
|
||||
auto [buffer, offset, invalidate] = texture_buffer.Map(max_size, sizeof(Common::Vec4f));
|
||||
|
||||
// helper function for SyncProcTexNoiseLUT/ColorMap/AlphaMap
|
||||
auto SyncProcTexValueLUT = [this, &buffer = buffer, &offset = offset, &invalidate = invalidate, &bytes_used](
|
||||
const std::array<Pica::State::ProcTex::ValueEntry, 128>& lut,
|
||||
auto SyncProcTexValueLUT =
|
||||
[this, &buffer = buffer, &offset = offset, &invalidate = invalidate,
|
||||
&bytes_used](const std::array<Pica::State::ProcTex::ValueEntry, 128>& lut,
|
||||
std::array<Common::Vec2f, 128>& lut_data, int& lut_offset) {
|
||||
std::array<Common::Vec2f, 128> new_data;
|
||||
std::transform(lut.begin(), lut.end(), new_data.begin(), [](const auto& entry) {
|
||||
@ -2130,20 +2141,22 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
||||
|
||||
u32 used_bytes = 0;
|
||||
const u32 uniform_size = static_cast<u32>(uniform_size_aligned_vs + uniform_size_aligned_fs);
|
||||
auto [uniforms, offset, invalidate] = uniform_buffer.Map(uniform_size,
|
||||
static_cast<u32>(uniform_buffer_alignment));
|
||||
auto [uniforms, offset, invalidate] =
|
||||
uniform_buffer.Map(uniform_size, static_cast<u32>(uniform_buffer_alignment));
|
||||
|
||||
if (sync_vs) {
|
||||
Pica::Shader::VSUniformData vs_uniforms;
|
||||
vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs);
|
||||
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
||||
|
||||
pipeline_cache.BindBuffer(0, uniform_buffer.GetHandle(), offset + used_bytes, sizeof(vs_uniforms));
|
||||
pipeline_cache.BindBuffer(0, uniform_buffer.GetHandle(), offset + used_bytes,
|
||||
sizeof(vs_uniforms));
|
||||
used_bytes += static_cast<u32>(uniform_size_aligned_vs);
|
||||
}
|
||||
|
||||
if (sync_fs || invalidate) {
|
||||
std::memcpy(uniforms + used_bytes, &uniform_block_data.data, sizeof(Pica::Shader::UniformData));
|
||||
std::memcpy(uniforms + used_bytes, &uniform_block_data.data,
|
||||
sizeof(Pica::Shader::UniformData));
|
||||
|
||||
pipeline_cache.BindBuffer(1, uniform_buffer.GetHandle(), offset + used_bytes,
|
||||
sizeof(uniform_block_data.data));
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/regs_lighting.h"
|
||||
#include "video_core/regs_texturing.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
#include "video_core/shader/shader_uniforms.h"
|
||||
@ -55,7 +55,7 @@ struct FramebufferInfo {
|
||||
auto operator<=>(const FramebufferInfo&) const noexcept = default;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Vulkan
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
@ -77,9 +77,11 @@ namespace Vulkan {
|
||||
|
||||
class RasterizerVulkan : public VideoCore::RasterizerAccelerated {
|
||||
friend class RendererVulkan;
|
||||
|
||||
public:
|
||||
explicit RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instance& instance, TaskScheduler& scheduler,
|
||||
TextureRuntime& runtime, RenderpassCache& renderpass_cache);
|
||||
explicit RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instance& instance,
|
||||
TaskScheduler& scheduler, TextureRuntime& runtime,
|
||||
RenderpassCache& renderpass_cache);
|
||||
~RasterizerVulkan() override;
|
||||
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
|
@ -4,29 +4,39 @@
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
VideoCore::PixelFormat ToFormatColor(u32 index) {
|
||||
switch (index) {
|
||||
case 0: return VideoCore::PixelFormat::RGBA8;
|
||||
case 1: return VideoCore::PixelFormat::RGB8;
|
||||
case 2: return VideoCore::PixelFormat::RGB5A1;
|
||||
case 3: return VideoCore::PixelFormat::RGB565;
|
||||
case 4: return VideoCore::PixelFormat::RGBA4;
|
||||
default: return VideoCore::PixelFormat::Invalid;
|
||||
case 0:
|
||||
return VideoCore::PixelFormat::RGBA8;
|
||||
case 1:
|
||||
return VideoCore::PixelFormat::RGB8;
|
||||
case 2:
|
||||
return VideoCore::PixelFormat::RGB5A1;
|
||||
case 3:
|
||||
return VideoCore::PixelFormat::RGB565;
|
||||
case 4:
|
||||
return VideoCore::PixelFormat::RGBA4;
|
||||
default:
|
||||
return VideoCore::PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
VideoCore::PixelFormat ToFormatDepth(u32 index) {
|
||||
switch (index) {
|
||||
case 0: return VideoCore::PixelFormat::D16;
|
||||
case 1: return VideoCore::PixelFormat::D24;
|
||||
case 3: return VideoCore::PixelFormat::D24S8;
|
||||
default: return VideoCore::PixelFormat::Invalid;
|
||||
case 0:
|
||||
return VideoCore::PixelFormat::D16;
|
||||
case 1:
|
||||
return VideoCore::PixelFormat::D24;
|
||||
case 3:
|
||||
return VideoCore::PixelFormat::D24S8;
|
||||
default:
|
||||
return VideoCore::PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,14 +57,12 @@ RenderpassCache::RenderpassCache(const Instance& instance, TaskScheduler& schedu
|
||||
continue;
|
||||
}
|
||||
|
||||
cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_format,
|
||||
vk::AttachmentLoadOp::eLoad,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal);
|
||||
cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_format,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal);
|
||||
cached_renderpasses[color][depth][0] = CreateRenderPass(
|
||||
color_format, depth_format, vk::AttachmentLoadOp::eLoad,
|
||||
vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
cached_renderpasses[color][depth][1] = CreateRenderPass(
|
||||
color_format, depth_format, vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,26 +110,28 @@ void RenderpassCache::ExitRenderpass() {
|
||||
|
||||
void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
|
||||
if (!present_renderpass) {
|
||||
present_renderpass = CreateRenderPass(format, vk::Format::eUndefined,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::ePresentSrcKHR);
|
||||
present_renderpass =
|
||||
CreateRenderPass(format, vk::Format::eUndefined, vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
|
||||
}
|
||||
}
|
||||
|
||||
vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
|
||||
bool is_clear) const {
|
||||
vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color,
|
||||
VideoCore::PixelFormat depth, bool is_clear) const {
|
||||
const u32 color_index =
|
||||
color == VideoCore::PixelFormat::Invalid ? MAX_COLOR_FORMATS : static_cast<u32>(color);
|
||||
const u32 depth_index =
|
||||
depth == VideoCore::PixelFormat::Invalid ? MAX_DEPTH_FORMATS : (static_cast<u32>(depth) - 14);
|
||||
const u32 depth_index = depth == VideoCore::PixelFormat::Invalid
|
||||
? MAX_DEPTH_FORMATS
|
||||
: (static_cast<u32>(depth) - 14);
|
||||
|
||||
ASSERT(color_index <= MAX_COLOR_FORMATS && depth_index <= MAX_DEPTH_FORMATS);
|
||||
return cached_renderpasses[color_index][depth_index][is_clear];
|
||||
}
|
||||
|
||||
vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op,
|
||||
vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const {
|
||||
vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth,
|
||||
vk::AttachmentLoadOp load_op,
|
||||
vk::ImageLayout initial_layout,
|
||||
vk::ImageLayout final_layout) const {
|
||||
// Define attachments
|
||||
u32 attachment_count = 0;
|
||||
std::array<vk::AttachmentDescription, 2> attachments;
|
||||
@ -132,20 +142,17 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
|
||||
vk::AttachmentReference depth_attachment_ref{};
|
||||
|
||||
if (color != vk::Format::eUndefined) {
|
||||
attachments[attachment_count] = vk::AttachmentDescription{
|
||||
.format = color,
|
||||
attachments[attachment_count] =
|
||||
vk::AttachmentDescription{.format = color,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
||||
.initialLayout = initial_layout,
|
||||
.finalLayout = final_layout
|
||||
};
|
||||
.finalLayout = final_layout};
|
||||
|
||||
color_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = attachment_count++,
|
||||
.layout = vk::ImageLayout::eColorAttachmentOptimal
|
||||
};
|
||||
.attachment = attachment_count++, .layout = vk::ImageLayout::eColorAttachmentOptimal};
|
||||
|
||||
use_color = true;
|
||||
}
|
||||
@ -158,40 +165,35 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
|
||||
.stencilLoadOp = vk::AttachmentLoadOp::eLoad,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
|
||||
.initialLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||
};
|
||||
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal};
|
||||
|
||||
depth_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = attachment_count++,
|
||||
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||
};
|
||||
depth_attachment_ref =
|
||||
vk::AttachmentReference{.attachment = attachment_count++,
|
||||
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal};
|
||||
|
||||
use_depth = true;
|
||||
}
|
||||
|
||||
// We also require only one subpass
|
||||
const vk::SubpassDescription subpass = {
|
||||
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||
const vk::SubpassDescription subpass = {.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = use_color ? 1u : 0u,
|
||||
.pColorAttachments = &color_attachment_ref,
|
||||
.pResolveAttachments = 0,
|
||||
.pDepthStencilAttachment = use_depth ? &depth_attachment_ref : nullptr
|
||||
};
|
||||
.pDepthStencilAttachment =
|
||||
use_depth ? &depth_attachment_ref : nullptr};
|
||||
|
||||
const vk::RenderPassCreateInfo renderpass_info = {
|
||||
.attachmentCount = attachment_count,
|
||||
const vk::RenderPassCreateInfo renderpass_info = {.attachmentCount = attachment_count,
|
||||
.pAttachments = attachments.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 0,
|
||||
.pDependencies = nullptr
|
||||
};
|
||||
.pDependencies = nullptr};
|
||||
|
||||
// Create the renderpass
|
||||
vk::Device device = instance.GetDevice();
|
||||
return device.createRenderPass(renderpass_info);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Vulkan
|
||||
} // namespace Vulkan
|
||||
|
@ -30,8 +30,8 @@ public:
|
||||
void CreatePresentRenderpass(vk::Format format);
|
||||
|
||||
/// Returns the renderpass associated with the color-depth format pair
|
||||
[[nodiscard]] vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
|
||||
bool is_clear) const;
|
||||
[[nodiscard]] vk::RenderPass GetRenderpass(VideoCore::PixelFormat color,
|
||||
VideoCore::PixelFormat depth, bool is_clear) const;
|
||||
/// Returns the swapchain clear renderpass
|
||||
[[nodiscard]] vk::RenderPass GetPresentRenderpass() const {
|
||||
return present_renderpass;
|
||||
@ -44,8 +44,9 @@ public:
|
||||
|
||||
private:
|
||||
/// Creates a renderpass configured appropriately and stores it in cached_renderpasses
|
||||
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op,
|
||||
vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const;
|
||||
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth,
|
||||
vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout,
|
||||
vk::ImageLayout final_layout) const;
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
@ -53,7 +54,7 @@ private:
|
||||
|
||||
vk::RenderPass active_renderpass = VK_NULL_HANDLE;
|
||||
vk::RenderPass present_renderpass{};
|
||||
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS+1][MAX_DEPTH_FORMATS+1][2];
|
||||
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -3,17 +3,16 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include <SPIRV/GlslangToSpv.h>
|
||||
#include <glslang/Include/ResourceLimits.h>
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader.h"
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/Include/ResourceLimits.h>
|
||||
#include <SPIRV/GlslangToSpv.h>
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
constexpr TBuiltInResource DefaultTBuiltInResource = {
|
||||
.maxLights = 32,
|
||||
constexpr TBuiltInResource DefaultTBuiltInResource = {.maxLights = 32,
|
||||
.maxClipPlanes = 6,
|
||||
.maxTextureUnits = 32,
|
||||
.maxTextureCoords = 32,
|
||||
@ -92,7 +91,8 @@ constexpr TBuiltInResource DefaultTBuiltInResource = {
|
||||
.maxCombinedAtomicCounterBuffers = 1,
|
||||
.maxAtomicCounterBufferSize = 16384,
|
||||
.maxTransformFeedbackBuffers = 4,
|
||||
.maxTransformFeedbackInterleavedComponents = 64,
|
||||
.maxTransformFeedbackInterleavedComponents =
|
||||
64,
|
||||
.maxCullDistances = 8,
|
||||
.maxCombinedClipAndCullDistances = 8,
|
||||
.maxSamples = 4,
|
||||
@ -116,8 +116,7 @@ constexpr TBuiltInResource DefaultTBuiltInResource = {
|
||||
.generalSamplerIndexing = 1,
|
||||
.generalVariableIndexing = 1,
|
||||
.generalConstantMatrixVectorIndexing = 1,
|
||||
}
|
||||
};
|
||||
}};
|
||||
|
||||
EShLanguage ToEshShaderStage(vk::ShaderStageFlagBits stage) {
|
||||
switch (stage) {
|
||||
@ -162,7 +161,8 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||
}
|
||||
|
||||
EProfile profile = ECoreProfile;
|
||||
EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
|
||||
EShMessages messages =
|
||||
static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
|
||||
EShLanguage lang = ToEshShaderStage(stage);
|
||||
|
||||
int default_version = 450;
|
||||
@ -170,12 +170,15 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||
int pass_source_code_length = static_cast<int>(code.size());
|
||||
|
||||
auto shader = std::make_unique<glslang::TShader>(lang);
|
||||
shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
|
||||
shader->setEnvTarget(glslang::EShTargetSpv,
|
||||
glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
|
||||
shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
|
||||
|
||||
glslang::TShader::ForbidIncluder includer;
|
||||
if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages, includer)) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(), shader->getInfoDebugLog());
|
||||
if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages,
|
||||
includer)) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(),
|
||||
shader->getInfoDebugLog());
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
@ -183,7 +186,8 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||
auto program = std::make_unique<glslang::TProgram>();
|
||||
program->addShader(shader.get());
|
||||
if (!program->link(messages)) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(), program->getInfoDebugLog());
|
||||
LOG_CRITICAL(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(),
|
||||
program->getInfoDebugLog());
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
@ -212,10 +216,8 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v
|
||||
LOG_INFO(Render_Vulkan, "SPIR-V conversion messages: {}", spv_messages);
|
||||
}
|
||||
|
||||
const vk::ShaderModuleCreateInfo shader_info = {
|
||||
.codeSize = out_code.size() * sizeof(u32),
|
||||
.pCode = out_code.data()
|
||||
};
|
||||
const vk::ShaderModuleCreateInfo shader_info = {.codeSize = out_code.size() * sizeof(u32),
|
||||
.pCode = out_code.data()};
|
||||
|
||||
return device.createShaderModule(shader_info);
|
||||
}
|
||||
|
@ -8,12 +8,9 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
enum class ShaderOptimization {
|
||||
High = 0,
|
||||
Debug = 1
|
||||
};
|
||||
enum class ShaderOptimization { High = 0, Debug = 1 };
|
||||
|
||||
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage,
|
||||
vk::Device device, ShaderOptimization level);
|
||||
vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device,
|
||||
ShaderOptimization level);
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -293,7 +293,8 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||
// Only unit 0 respects the texturing type
|
||||
switch (state.texture0_type) {
|
||||
case TexturingRegs::TextureConfig::Texture2D:
|
||||
return "textureLod(sampler2D(tex0, tex0_sampler), texcoord0, getLod(texcoord0 * vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))))";
|
||||
return "textureLod(sampler2D(tex0, tex0_sampler), texcoord0, getLod(texcoord0 * "
|
||||
"vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))))";
|
||||
case TexturingRegs::TextureConfig::Projection2D:
|
||||
// TODO (wwylele): find the exact LOD formula for projection texture
|
||||
return "textureProj(sampler2D(tex0, tex0_sampler), vec3(texcoord0, texcoord0_w))";
|
||||
@ -311,12 +312,15 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||
return "texture(sampler2D(tex0, tex0_sampler), texcoord0)";
|
||||
}
|
||||
case 1:
|
||||
return "textureLod(sampler2D(tex1, tex1_sampler), texcoord1, getLod(texcoord1 * vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))))";
|
||||
return "textureLod(sampler2D(tex1, tex1_sampler), texcoord1, getLod(texcoord1 * "
|
||||
"vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))))";
|
||||
case 2:
|
||||
if (state.texture2_use_coord1)
|
||||
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord1, getLod(texcoord1 * vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
|
||||
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord1, getLod(texcoord1 * "
|
||||
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
|
||||
else
|
||||
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord2, getLod(texcoord2 * vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
|
||||
return "textureLod(sampler2D(tex2, tex2_sampler), texcoord2, getLod(texcoord2 * "
|
||||
"vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))";
|
||||
case 3:
|
||||
if (state.proctex.enable) {
|
||||
return "ProcTex()";
|
||||
@ -1580,8 +1584,8 @@ void main() {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<std::string> GenerateVertexShader(
|
||||
const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config) {
|
||||
std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
|
||||
const PicaVSConfig& config) {
|
||||
/*std::string out = "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||
out += ShaderDecompiler::GetCommonDeclarations();
|
||||
|
||||
@ -1626,7 +1630,8 @@ layout (std140) uniform vs_config {
|
||||
|
||||
// output attributes declaration
|
||||
for (u32 i = 0; i < config.state.num_outputs; ++i) {
|
||||
out += "layout(location = " + std::to_string(i) + ") out vec4 vs_out_attr" + std::to_string(i) + ";\n";
|
||||
out += "layout(location = " + std::to_string(i) + ") out vec4 vs_out_attr" +
|
||||
std::to_string(i) + ";\n";
|
||||
}
|
||||
|
||||
out += "\nvoid main() {\n";
|
||||
@ -1648,7 +1653,8 @@ static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config) {
|
||||
|
||||
out += '\n';
|
||||
for (u32 i = 0; i < config.vs_output_attributes; ++i) {
|
||||
out += ("layout(location = " + std::to_string(i) + ") in vec4 vs_out_attr" + std::to_string(i) + "[];\n";
|
||||
out += ("layout(location = " + std::to_string(i) + ") in vec4 vs_out_attr" +
|
||||
std::to_string(i) + "[];\n";
|
||||
}
|
||||
|
||||
out += R"(
|
||||
|
@ -203,8 +203,8 @@ std::string GenerateTrivialVertexShader();
|
||||
* Generates the GLSL vertex shader program source code for the given VS program
|
||||
* @returns String of the shader source code; boost::none on failure
|
||||
*/
|
||||
std::optional<std::string> GenerateVertexShader(
|
||||
const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config);
|
||||
std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
|
||||
const PicaVSConfig& config);
|
||||
|
||||
/**
|
||||
* Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
|
||||
|
@ -7,9 +7,9 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -21,8 +21,8 @@ inline auto ToVkAccessStageFlags(vk::BufferUsageFlagBits usage) {
|
||||
vk::PipelineStageFlagBits::eVertexInput);
|
||||
break;
|
||||
case vk::BufferUsageFlagBits::eIndexBuffer:
|
||||
result = std::make_pair(vk::AccessFlagBits::eIndexRead,
|
||||
vk::PipelineStageFlagBits::eVertexInput);
|
||||
result =
|
||||
std::make_pair(vk::AccessFlagBits::eIndexRead, vk::PipelineStageFlagBits::eVertexInput);
|
||||
case vk::BufferUsageFlagBits::eUniformBuffer:
|
||||
result = std::make_pair(vk::AccessFlagBits::eUniformRead,
|
||||
vk::PipelineStageFlagBits::eVertexShader |
|
||||
@ -41,24 +41,20 @@ inline auto ToVkAccessStageFlags(vk::BufferUsageFlagBits usage) {
|
||||
|
||||
StagingBuffer::StagingBuffer(const Instance& instance, u32 size, vk::BufferUsageFlags usage)
|
||||
: instance{instance} {
|
||||
const vk::BufferCreateInfo buffer_info = {
|
||||
.size = size,
|
||||
.usage = usage
|
||||
};
|
||||
const vk::BufferCreateInfo buffer_info = {.size = size, .usage = usage};
|
||||
|
||||
const VmaAllocationCreateInfo alloc_create_info = {
|
||||
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||
VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST
|
||||
};
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST};
|
||||
|
||||
VkBuffer unsafe_buffer = VK_NULL_HANDLE;
|
||||
VkBufferCreateInfo unsafe_buffer_info = static_cast<VkBufferCreateInfo>(buffer_info);
|
||||
VmaAllocationInfo alloc_info;
|
||||
VmaAllocator allocator = instance.GetAllocator();
|
||||
|
||||
vmaCreateBuffer(allocator, &unsafe_buffer_info, &alloc_create_info,
|
||||
&unsafe_buffer, &allocation, &alloc_info);
|
||||
vmaCreateBuffer(allocator, &unsafe_buffer_info, &alloc_create_info, &unsafe_buffer, &allocation,
|
||||
&alloc_info);
|
||||
|
||||
buffer = vk::Buffer{unsafe_buffer};
|
||||
mapped = std::span{reinterpret_cast<std::byte*>(alloc_info.pMappedData), size};
|
||||
@ -68,27 +64,25 @@ StagingBuffer::~StagingBuffer() {
|
||||
vmaDestroyBuffer(instance.GetAllocator(), static_cast<VkBuffer>(buffer), allocation);
|
||||
}
|
||||
|
||||
StreamBuffer::StreamBuffer(const Instance& instance, TaskScheduler& scheduler,
|
||||
u32 size, vk::BufferUsageFlagBits usage, std::span<const vk::Format> view_formats)
|
||||
: instance{instance}, scheduler{scheduler}, staging{instance, size, vk::BufferUsageFlagBits::eTransferSrc},
|
||||
StreamBuffer::StreamBuffer(const Instance& instance, TaskScheduler& scheduler, u32 size,
|
||||
vk::BufferUsageFlagBits usage, std::span<const vk::Format> view_formats)
|
||||
: instance{instance}, scheduler{scheduler}, staging{instance, size,
|
||||
vk::BufferUsageFlagBits::eTransferSrc},
|
||||
usage{usage}, total_size{size} {
|
||||
|
||||
const vk::BufferCreateInfo buffer_info = {
|
||||
.size = total_size,
|
||||
.usage = usage | vk::BufferUsageFlagBits::eTransferDst
|
||||
};
|
||||
.size = total_size, .usage = usage | vk::BufferUsageFlagBits::eTransferDst};
|
||||
|
||||
const VmaAllocationCreateInfo alloc_create_info = {
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE
|
||||
};
|
||||
const VmaAllocationCreateInfo alloc_create_info = {.usage =
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE};
|
||||
|
||||
VkBuffer unsafe_buffer = VK_NULL_HANDLE;
|
||||
VkBufferCreateInfo unsafe_buffer_info = static_cast<VkBufferCreateInfo>(buffer_info);
|
||||
VmaAllocationInfo alloc_info;
|
||||
VmaAllocator allocator = instance.GetAllocator();
|
||||
|
||||
vmaCreateBuffer(allocator, &unsafe_buffer_info, &alloc_create_info,
|
||||
&unsafe_buffer, &allocation, &alloc_info);
|
||||
vmaCreateBuffer(allocator, &unsafe_buffer_info, &alloc_create_info, &unsafe_buffer, &allocation,
|
||||
&alloc_info);
|
||||
|
||||
buffer = vk::Buffer{unsafe_buffer};
|
||||
|
||||
@ -97,11 +91,7 @@ StreamBuffer::StreamBuffer(const Instance& instance, TaskScheduler& scheduler,
|
||||
vk::Device device = instance.GetDevice();
|
||||
for (std::size_t i = 0; i < view_formats.size(); i++) {
|
||||
const vk::BufferViewCreateInfo view_info = {
|
||||
.buffer = buffer,
|
||||
.format = view_formats[i],
|
||||
.offset = 0,
|
||||
.range = total_size
|
||||
};
|
||||
.buffer = buffer, .format = view_formats[i], .offset = 0, .range = total_size};
|
||||
|
||||
views[i] = device.createBufferView(view_info);
|
||||
}
|
||||
@ -138,7 +128,6 @@ std::tuple<u8*, u32, bool> StreamBuffer::Map(u32 size, u32 alignment) {
|
||||
const u32 buffer_offset = current_bucket * bucket_size + bucket.offset;
|
||||
u8* mapped = reinterpret_cast<u8*>(staging.mapped.data() + buffer_offset);
|
||||
return std::make_tuple(mapped, buffer_offset, invalidate);
|
||||
|
||||
}
|
||||
|
||||
void StreamBuffer::Commit(u32 size) {
|
||||
@ -156,10 +145,7 @@ void StreamBuffer::Flush() {
|
||||
|
||||
const u32 flush_start = current_bucket * bucket_size;
|
||||
const vk::BufferCopy copy_region = {
|
||||
.srcOffset = flush_start,
|
||||
.dstOffset = flush_start,
|
||||
.size = flush_size
|
||||
};
|
||||
.srcOffset = flush_start, .dstOffset = flush_start, .size = flush_size};
|
||||
|
||||
vmaFlushAllocation(allocator, allocation, flush_start, flush_size);
|
||||
command_buffer.copyBuffer(staging.buffer, buffer, copy_region);
|
||||
@ -173,8 +159,7 @@ void StreamBuffer::Flush() {
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = buffer,
|
||||
.offset = flush_start,
|
||||
.size = flush_size
|
||||
};
|
||||
.size = flush_size};
|
||||
|
||||
command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, stage_mask,
|
||||
vk::DependencyFlagBits::eByRegion, {}, buffer_barrier, {});
|
||||
|
@ -33,8 +33,8 @@ struct StagingBuffer {
|
||||
|
||||
class StreamBuffer {
|
||||
public:
|
||||
StreamBuffer(const Instance& instance, TaskScheduler& scheduler,
|
||||
u32 size, vk::BufferUsageFlagBits usage, std::span<const vk::Format> views);
|
||||
StreamBuffer(const Instance& instance, TaskScheduler& scheduler, u32 size,
|
||||
vk::BufferUsageFlagBits usage, std::span<const vk::Format> views);
|
||||
~StreamBuffer();
|
||||
|
||||
std::tuple<u8*, u32, bool> Map(u32 size, u32 alignment = 0);
|
||||
|
@ -5,9 +5,9 @@
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include <algorithm>
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -59,8 +59,7 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||
.preTransform = transform,
|
||||
.presentMode = present_mode,
|
||||
.clipped = true,
|
||||
.oldSwapchain = swapchain
|
||||
};
|
||||
.oldSwapchain = swapchain};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
vk::SwapchainKHR new_swapchain = device.createSwapchainKHR(swapchain_info);
|
||||
@ -87,34 +86,25 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||
.image = image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = surface_format.format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
.layerCount = 1}};
|
||||
|
||||
vk::ImageView image_view = device.createImageView(view_info);
|
||||
const std::array attachments{image_view};
|
||||
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {
|
||||
.renderPass = present_renderpass,
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {.renderPass = present_renderpass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = attachments.data(),
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.layers = 1
|
||||
};
|
||||
.layers = 1};
|
||||
|
||||
vk::Framebuffer framebuffer = device.createFramebuffer(framebuffer_info);
|
||||
|
||||
return Image{
|
||||
.image = image,
|
||||
.image_view = image_view,
|
||||
.framebuffer = framebuffer
|
||||
};
|
||||
return Image{.image = image, .image_view = image_view, .framebuffer = framebuffer};
|
||||
});
|
||||
}
|
||||
|
||||
@ -141,13 +131,11 @@ void Swapchain::AcquireNextImage(vk::Semaphore signal_acquired) {
|
||||
}
|
||||
|
||||
void Swapchain::Present(vk::Semaphore wait_for_present) {
|
||||
const vk::PresentInfoKHR present_info = {
|
||||
.waitSemaphoreCount = 1,
|
||||
const vk::PresentInfoKHR present_info = {.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &wait_for_present,
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = &swapchain,
|
||||
.pImageIndices = ¤t_image
|
||||
};
|
||||
.pImageIndices = ¤t_image};
|
||||
|
||||
vk::Queue present_queue = instance.GetPresentQueue();
|
||||
vk::Result result = present_queue.presentKHR(present_info);
|
||||
@ -196,9 +184,8 @@ void Swapchain::Configure(u32 width, u32 height) {
|
||||
|
||||
// FIFO is guaranteed by the Vulkan standard to be available
|
||||
present_mode = vk::PresentModeKHR::eFifo;
|
||||
auto iter = std::ranges::find_if(modes, [](vk::PresentModeKHR mode) {
|
||||
return vk::PresentModeKHR::eMailbox == mode;
|
||||
});
|
||||
auto iter = std::ranges::find_if(
|
||||
modes, [](vk::PresentModeKHR mode) { return vk::PresentModeKHR::eMailbox == mode; });
|
||||
|
||||
// Prefer Mailbox if present for lowest latency
|
||||
if (iter != modes.end()) {
|
||||
@ -210,8 +197,8 @@ void Swapchain::Configure(u32 width, u32 height) {
|
||||
extent = capabilities.currentExtent;
|
||||
|
||||
if (capabilities.currentExtent.width == std::numeric_limits<u32>::max()) {
|
||||
extent.width = std::clamp(width, capabilities.minImageExtent.width,
|
||||
capabilities.maxImageExtent.width);
|
||||
extent.width =
|
||||
std::clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||
extent.height = std::clamp(height, capabilities.minImageExtent.height,
|
||||
capabilities.maxImageExtent.height);
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -16,8 +16,7 @@ TaskScheduler::TaskScheduler(const Instance& instance, RendererVulkan& renderer)
|
||||
vk::Device device = instance.GetDevice();
|
||||
const vk::CommandPoolCreateInfo command_pool_info = {
|
||||
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
||||
.queueFamilyIndex = instance.GetGraphicsQueueFamilyIndex()
|
||||
};
|
||||
.queueFamilyIndex = instance.GetGraphicsQueueFamilyIndex()};
|
||||
|
||||
command_pool = device.createCommandPool(command_pool_info);
|
||||
|
||||
@ -25,11 +24,8 @@ TaskScheduler::TaskScheduler(const Instance& instance, RendererVulkan& renderer)
|
||||
if (instance.IsTimelineSemaphoreSupported()) {
|
||||
const vk::StructureChain timeline_info = {
|
||||
vk::SemaphoreCreateInfo{},
|
||||
vk::SemaphoreTypeCreateInfo{
|
||||
.semaphoreType = vk::SemaphoreType::eTimeline,
|
||||
.initialValue = 0
|
||||
}
|
||||
};
|
||||
vk::SemaphoreTypeCreateInfo{.semaphoreType = vk::SemaphoreType::eTimeline,
|
||||
.initialValue = 0}};
|
||||
|
||||
timeline = device.createSemaphore(timeline_info.get());
|
||||
}
|
||||
@ -40,22 +36,19 @@ TaskScheduler::TaskScheduler(const Instance& instance, RendererVulkan& renderer)
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eSampledImage, 2048},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eCombinedImageSampler, 512},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eSampler, 2048},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformTexelBuffer, 1024}
|
||||
};
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformTexelBuffer, 1024}};
|
||||
|
||||
const vk::DescriptorPoolCreateInfo descriptor_pool_info = {
|
||||
.maxSets = 2048,
|
||||
.poolSizeCount = static_cast<u32>(pool_sizes.size()),
|
||||
.pPoolSizes = pool_sizes.data()
|
||||
};
|
||||
.pPoolSizes = pool_sizes.data()};
|
||||
|
||||
persistent_descriptor_pool = device.createDescriptorPool(descriptor_pool_info);
|
||||
|
||||
const vk::CommandBufferAllocateInfo buffer_info = {
|
||||
.commandPool = command_pool,
|
||||
const vk::CommandBufferAllocateInfo buffer_info = {.commandPool = command_pool,
|
||||
.level = vk::CommandBufferLevel::ePrimary,
|
||||
.commandBufferCount = 2 * SCHEDULER_COMMAND_COUNT
|
||||
};
|
||||
.commandBufferCount =
|
||||
2 * SCHEDULER_COMMAND_COUNT};
|
||||
|
||||
const auto command_buffers = device.allocateCommandBuffers(buffer_info);
|
||||
for (std::size_t i = 0; i < commands.size(); i++) {
|
||||
@ -70,8 +63,7 @@ TaskScheduler::TaskScheduler(const Instance& instance, RendererVulkan& renderer)
|
||||
}
|
||||
|
||||
const vk::CommandBufferBeginInfo begin_info = {
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
|
||||
};
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
|
||||
// Begin first command
|
||||
auto& command = commands[current_command];
|
||||
@ -106,13 +98,11 @@ void TaskScheduler::Synchronize(u32 slot) {
|
||||
if (command.fence_counter > completed_counter) {
|
||||
if (instance.IsTimelineSemaphoreSupported()) {
|
||||
const vk::SemaphoreWaitInfo wait_info = {
|
||||
.semaphoreCount = 1,
|
||||
.pSemaphores = &timeline,
|
||||
.pValues = &command.fence_counter
|
||||
};
|
||||
.semaphoreCount = 1, .pSemaphores = &timeline, .pValues = &command.fence_counter};
|
||||
|
||||
if (device.waitSemaphores(wait_info, UINT64_MAX) != vk::Result::eSuccess) {
|
||||
LOG_ERROR(Render_Vulkan, "Waiting for fence counter {} failed!", command.fence_counter);
|
||||
LOG_ERROR(Render_Vulkan, "Waiting for fence counter {} failed!",
|
||||
command.fence_counter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@ -161,8 +151,7 @@ void TaskScheduler::Submit(SubmitMode mode) {
|
||||
.waitSemaphoreValueCount = wait_semaphore_count,
|
||||
.pWaitSemaphoreValues = wait_values.data(),
|
||||
.signalSemaphoreValueCount = signal_semaphore_count,
|
||||
.pSignalSemaphoreValues = signal_values.data()
|
||||
};
|
||||
.pSignalSemaphoreValues = signal_values.data()};
|
||||
|
||||
const std::array<vk::PipelineStageFlags, 2> wait_stage_masks = {
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
@ -228,8 +217,7 @@ vk::CommandBuffer TaskScheduler::GetUploadCommandBuffer() {
|
||||
auto& command = commands[current_command];
|
||||
if (!command.use_upload_buffer) {
|
||||
const vk::CommandBufferBeginInfo begin_info = {
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
|
||||
};
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
|
||||
command.upload_command_buffer.begin(begin_info);
|
||||
command.use_upload_buffer = true;
|
||||
@ -246,8 +234,7 @@ void TaskScheduler::SwitchSlot() {
|
||||
Synchronize(current_command);
|
||||
|
||||
const vk::CommandBufferBeginInfo begin_info = {
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
|
||||
};
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
|
||||
// Begin the next command buffer.
|
||||
command.render_command_buffer.begin(begin_info);
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include "common/common_types.h"
|
||||
#include <memory>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
@ -42,12 +42,14 @@ TextureRuntime::TextureRuntime(const Instance& instance, TaskScheduler& schedule
|
||||
vk::BufferUsageFlagBits::eTransferDst);
|
||||
}
|
||||
|
||||
auto Register = [this](VideoCore::PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
auto Register = [this](VideoCore::PixelFormat dest,
|
||||
std::unique_ptr<FormatReinterpreterBase>&& obj) {
|
||||
const u32 dst_index = static_cast<u32>(dest);
|
||||
return reinterpreters[dst_index].push_back(std::move(obj));
|
||||
};
|
||||
|
||||
Register(VideoCore::PixelFormat::RGBA8, std::make_unique<D24S8toRGBA8>(instance, scheduler, *this));
|
||||
Register(VideoCore::PixelFormat::RGBA8,
|
||||
std::make_unique<D24S8toRGBA8>(instance, scheduler, *this));
|
||||
}
|
||||
|
||||
TextureRuntime::~TextureRuntime() {
|
||||
@ -81,12 +83,10 @@ StagingData TextureRuntime::FindStaging(u32 size, bool upload) {
|
||||
}
|
||||
|
||||
const auto& buffer = staging_buffers[current_slot];
|
||||
return StagingData{
|
||||
.buffer = buffer->buffer,
|
||||
return StagingData{.buffer = buffer->buffer,
|
||||
.size = size,
|
||||
.mapped = buffer->mapped.subspan(offset, size),
|
||||
.buffer_offset = offset
|
||||
};
|
||||
.buffer_offset = offset};
|
||||
}
|
||||
|
||||
void TextureRuntime::Finish() {
|
||||
@ -102,11 +102,7 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
|
||||
const u32 layers = type == VideoCore::TextureType::CubeMap ? 6 : 1;
|
||||
const VideoCore::HostTextureTag key = {
|
||||
.format = format,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = layers
|
||||
};
|
||||
.format = format, .width = width, .height = height, .layers = layers};
|
||||
|
||||
// Attempt to recycle an unused allocation
|
||||
if (auto it = texture_recycler.find(key); it != texture_recycler.end()) {
|
||||
@ -123,22 +119,18 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
const vk::ImageUsageFlags vk_usage = is_suitable ? traits.usage : GetImageUsage(aspect);
|
||||
|
||||
const u32 levels = std::bit_width(std::max(width, height));
|
||||
const vk::ImageCreateInfo image_info = {
|
||||
.flags = type == VideoCore::TextureType::CubeMap ?
|
||||
vk::ImageCreateFlagBits::eCubeCompatible :
|
||||
vk::ImageCreateFlags{},
|
||||
const vk::ImageCreateInfo image_info = {.flags = type == VideoCore::TextureType::CubeMap
|
||||
? vk::ImageCreateFlagBits::eCubeCompatible
|
||||
: vk::ImageCreateFlags{},
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = vk_format,
|
||||
.extent = {width, height, 1},
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = layers,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.usage = vk_usage
|
||||
};
|
||||
.usage = vk_usage};
|
||||
|
||||
const VmaAllocationCreateInfo alloc_info = {
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE
|
||||
};
|
||||
const VmaAllocationCreateInfo alloc_info = {.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE};
|
||||
|
||||
VkImage unsafe_image{};
|
||||
VkImageCreateInfo unsafe_image_info = static_cast<VkImageCreateInfo>(image_info);
|
||||
@ -152,36 +144,28 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
}
|
||||
|
||||
vk::Image image = vk::Image{unsafe_image};
|
||||
const vk::ImageViewCreateInfo view_info = {
|
||||
.image = image,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap ?
|
||||
vk::ImageViewType::eCube :
|
||||
vk::ImageViewType::e2D,
|
||||
const vk::ImageViewCreateInfo view_info = {.image = image,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap
|
||||
? vk::ImageViewType::eCube
|
||||
: vk::ImageViewType::e2D,
|
||||
.format = vk_format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = aspect,
|
||||
.subresourceRange = {.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = levels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = layers
|
||||
}
|
||||
};
|
||||
.layerCount = layers}};
|
||||
|
||||
// Also create a base mip view in case this is used as an attachment
|
||||
const vk::ImageViewCreateInfo base_view_info = {
|
||||
.image = image,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap ?
|
||||
vk::ImageViewType::eCube :
|
||||
vk::ImageViewType::e2D,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap ? vk::ImageViewType::eCube
|
||||
: vk::ImageViewType::e2D,
|
||||
.format = vk_format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = aspect,
|
||||
.subresourceRange = {.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = layers
|
||||
}
|
||||
};
|
||||
.layerCount = layers}};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
vk::ImageView image_view = device.createImageView(view_info);
|
||||
@ -193,26 +177,21 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
if (format == VideoCore::PixelFormat::D24S8) {
|
||||
vk::ImageViewCreateInfo view_info = {
|
||||
.image = image,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap ?
|
||||
vk::ImageViewType::eCube :
|
||||
vk::ImageViewType::e2D,
|
||||
.viewType = type == VideoCore::TextureType::CubeMap ? vk::ImageViewType::eCube
|
||||
: vk::ImageViewType::e2D,
|
||||
.format = vk_format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
||||
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = levels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = layers
|
||||
}
|
||||
};
|
||||
.layerCount = layers}};
|
||||
|
||||
depth_view = device.createImageView(view_info);
|
||||
view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eStencil;
|
||||
stencil_view = device.createImageView(view_info);
|
||||
}
|
||||
|
||||
return ImageAlloc{
|
||||
.image = image,
|
||||
return ImageAlloc{.image = image,
|
||||
.image_view = image_view,
|
||||
.base_view = base_view,
|
||||
.depth_view = depth_view,
|
||||
@ -221,16 +200,15 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
.format = vk_format,
|
||||
.aspect = aspect,
|
||||
.levels = levels,
|
||||
.layers = layers
|
||||
};
|
||||
.layers = layers};
|
||||
}
|
||||
|
||||
void TextureRuntime::Recycle(const VideoCore::HostTextureTag tag, ImageAlloc&& alloc) {
|
||||
texture_recycler.emplace(tag, std::move(alloc));
|
||||
}
|
||||
|
||||
void TextureRuntime::FormatConvert(const Surface& surface, bool upload,
|
||||
std::span<std::byte> source, std::span<std::byte> dest) {
|
||||
void TextureRuntime::FormatConvert(const Surface& surface, bool upload, std::span<std::byte> source,
|
||||
std::span<std::byte> dest) {
|
||||
if (!surface.NeedsConvert()) {
|
||||
std::memcpy(dest.data(), source.data(), source.size());
|
||||
return;
|
||||
@ -267,7 +245,8 @@ void TextureRuntime::FormatConvert(const Surface& surface, bool upload,
|
||||
}
|
||||
|
||||
LOG_WARNING(Render_Vulkan, "Missing format convertion: {} {} {}",
|
||||
vk::to_string(surface.traits.native), upload ? "->" : "<-", vk::to_string(surface.alloc.format));
|
||||
vk::to_string(surface.traits.native), upload ? "->" : "<-",
|
||||
vk::to_string(surface.alloc.format));
|
||||
}
|
||||
|
||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||
@ -276,36 +255,37 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
renderpass_cache.ExitRenderpass();
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal,
|
||||
0, surface.alloc.levels, 0, surface.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal, 0,
|
||||
surface.alloc.levels, 0,
|
||||
surface.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
|
||||
vk::ClearValue clear_value{};
|
||||
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||
clear_value.color = vk::ClearColorValue{
|
||||
.float32 = std::to_array({value.color[0], value.color[1], value.color[2], value.color[3]})
|
||||
};
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
clear_value.depthStencil = vk::ClearDepthStencilValue{
|
||||
.depth = value.depth,
|
||||
.stencil = value.stencil
|
||||
};
|
||||
.float32 =
|
||||
std::to_array({value.color[0], value.color[1], value.color[2], value.color[3]})};
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth ||
|
||||
aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
clear_value.depthStencil =
|
||||
vk::ClearDepthStencilValue{.depth = value.depth, .stencil = value.stencil};
|
||||
}
|
||||
|
||||
// For full clears we can use vkCmdClearColorImage/vkCmdClearDepthStencilImage
|
||||
if (clear.texture_rect == surface.GetScaledRect()) {
|
||||
const vk::ImageSubresourceRange range = {
|
||||
.aspectMask = aspect,
|
||||
const vk::ImageSubresourceRange range = {.aspectMask = aspect,
|
||||
.baseMipLevel = clear.texture_level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
};
|
||||
.layerCount = 1};
|
||||
|
||||
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||
command_buffer.clearColorImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
clear_value.color, range);
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
command_buffer.clearDepthStencilImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
command_buffer.clearColorImage(surface.alloc.image,
|
||||
vk::ImageLayout::eTransferDstOptimal, clear_value.color,
|
||||
range);
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth ||
|
||||
aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
command_buffer.clearDepthStencilImage(surface.alloc.image,
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
clear_value.depthStencil, range);
|
||||
}
|
||||
} else {
|
||||
@ -313,26 +293,28 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
vk::RenderPass clear_renderpass{};
|
||||
ImageAlloc& alloc = surface.alloc;
|
||||
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||
clear_renderpass = renderpass_cache.GetRenderpass(surface.pixel_format,
|
||||
VideoCore::PixelFormat::Invalid, true);
|
||||
Transition(command_buffer, alloc, vk::ImageLayout::eColorAttachmentOptimal, 0, alloc.levels);
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
clear_renderpass = renderpass_cache.GetRenderpass(
|
||||
surface.pixel_format, VideoCore::PixelFormat::Invalid, true);
|
||||
Transition(command_buffer, alloc, vk::ImageLayout::eColorAttachmentOptimal, 0,
|
||||
alloc.levels);
|
||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth ||
|
||||
aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||
clear_renderpass = renderpass_cache.GetRenderpass(VideoCore::PixelFormat::Invalid,
|
||||
surface.pixel_format, true);
|
||||
Transition(command_buffer, alloc, vk::ImageLayout::eDepthStencilAttachmentOptimal, 0, alloc.levels);
|
||||
Transition(command_buffer, alloc, vk::ImageLayout::eDepthStencilAttachmentOptimal, 0,
|
||||
alloc.levels);
|
||||
}
|
||||
|
||||
auto [it, new_framebuffer] = clear_framebuffers.try_emplace(alloc.image_view, vk::Framebuffer{});
|
||||
if (new_framebuffer) {\
|
||||
auto [it, new_framebuffer] =
|
||||
clear_framebuffers.try_emplace(alloc.image_view, vk::Framebuffer{});
|
||||
if (new_framebuffer) {
|
||||
const vk::ImageView framebuffer_view = surface.GetFramebufferView();
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {
|
||||
.renderPass = clear_renderpass,
|
||||
const vk::FramebufferCreateInfo framebuffer_info = {.renderPass = clear_renderpass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &framebuffer_view,
|
||||
.width = surface.GetScaledWidth(),
|
||||
.height = surface.GetScaledHeight(),
|
||||
.layers = 1
|
||||
};
|
||||
.layers = 1};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
it->second = device.createFramebuffer(framebuffer_info);
|
||||
@ -341,13 +323,12 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
const vk::RenderPassBeginInfo clear_begin_info = {
|
||||
.renderPass = clear_renderpass,
|
||||
.framebuffer = it->second,
|
||||
.renderArea = vk::Rect2D{
|
||||
.offset = {static_cast<s32>(clear.texture_rect.left), static_cast<s32>(clear.texture_rect.bottom)},
|
||||
.extent = {clear.texture_rect.GetWidth(), clear.texture_rect.GetHeight()}
|
||||
},
|
||||
.renderArea = vk::Rect2D{.offset = {static_cast<s32>(clear.texture_rect.left),
|
||||
static_cast<s32>(clear.texture_rect.bottom)},
|
||||
.extent = {clear.texture_rect.GetWidth(),
|
||||
clear.texture_rect.GetHeight()}},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear_value
|
||||
};
|
||||
.pClearValues = &clear_value};
|
||||
|
||||
renderpass_cache.EnterRenderpass(clear_begin_info);
|
||||
renderpass_cache.ExitRenderpass();
|
||||
@ -356,30 +337,28 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy) {
|
||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureCopy& copy) {
|
||||
renderpass_cache.ExitRenderpass();
|
||||
|
||||
const vk::ImageCopy image_copy = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = ToVkAspect(source.type),
|
||||
.srcSubresource = {.aspectMask = ToVkAspect(source.type),
|
||||
.mipLevel = copy.src_level,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.srcOffset = {static_cast<s32>(copy.src_offset.x), static_cast<s32>(copy.src_offset.y), 0},
|
||||
.dstSubresource = {
|
||||
.aspectMask = ToVkAspect(dest.type),
|
||||
.dstSubresource = {.aspectMask = ToVkAspect(dest.type),
|
||||
.mipLevel = copy.dst_level,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.dstOffset = {static_cast<s32>(copy.dst_offset.x), static_cast<s32>(copy.dst_offset.y), 0},
|
||||
.extent = {copy.extent.width, copy.extent.height, 1}
|
||||
};
|
||||
.extent = {copy.extent.width, copy.extent.height, 1}};
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, 0, source.alloc.levels);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, 0, dest.alloc.levels);
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, 0,
|
||||
source.alloc.levels);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, 0,
|
||||
dest.alloc.levels);
|
||||
|
||||
command_buffer.copyImage(source.alloc.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
dest.alloc.image, vk::ImageLayout::eTransferDstOptimal, image_copy);
|
||||
@ -387,45 +366,41 @@ bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCor
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) {
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
renderpass_cache.ExitRenderpass();
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal,
|
||||
0, source.alloc.levels, 0, source.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal,
|
||||
0, dest.alloc.levels, 0, dest.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, 0,
|
||||
source.alloc.levels, 0,
|
||||
source.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, 0,
|
||||
dest.alloc.levels, 0, dest.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
|
||||
const std::array source_offsets = {
|
||||
vk::Offset3D{static_cast<s32>(blit.src_rect.left), static_cast<s32>(blit.src_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(blit.src_rect.right), static_cast<s32>(blit.src_rect.top), 1}
|
||||
};
|
||||
const std::array source_offsets = {vk::Offset3D{static_cast<s32>(blit.src_rect.left),
|
||||
static_cast<s32>(blit.src_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(blit.src_rect.right),
|
||||
static_cast<s32>(blit.src_rect.top), 1}};
|
||||
|
||||
const std::array dest_offsets = {
|
||||
vk::Offset3D{static_cast<s32>(blit.dst_rect.left), static_cast<s32>(blit.dst_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(blit.dst_rect.right), static_cast<s32>(blit.dst_rect.top), 1}
|
||||
};
|
||||
const std::array dest_offsets = {vk::Offset3D{static_cast<s32>(blit.dst_rect.left),
|
||||
static_cast<s32>(blit.dst_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(blit.dst_rect.right),
|
||||
static_cast<s32>(blit.dst_rect.top), 1}};
|
||||
|
||||
const vk::ImageBlit blit_area = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = ToVkAspect(source.type),
|
||||
const vk::ImageBlit blit_area = {.srcSubresource = {.aspectMask = ToVkAspect(source.type),
|
||||
.mipLevel = blit.src_level,
|
||||
.baseArrayLayer = blit.src_layer,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.srcOffsets = source_offsets,
|
||||
.dstSubresource = {
|
||||
.aspectMask = ToVkAspect(dest.type),
|
||||
.dstSubresource = {.aspectMask = ToVkAspect(dest.type),
|
||||
.mipLevel = blit.dst_level,
|
||||
.baseArrayLayer = blit.dst_layer,
|
||||
.layerCount = 1
|
||||
},
|
||||
.dstOffsets = dest_offsets
|
||||
};
|
||||
.layerCount = 1},
|
||||
.dstOffsets = dest_offsets};
|
||||
|
||||
command_buffer.blitImage(source.alloc.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
dest.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
blit_area, vk::Filter::eNearest);
|
||||
dest.alloc.image, vk::ImageLayout::eTransferDstOptimal, blit_area,
|
||||
vk::Filter::eNearest);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -444,33 +419,23 @@ void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
|
||||
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferSrcOptimal, i - 1, 1);
|
||||
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal, i, 1);
|
||||
|
||||
const std::array source_offsets = {
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{current_width, current_height, 1}
|
||||
};
|
||||
const std::array source_offsets = {vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{current_width, current_height, 1}};
|
||||
|
||||
const std::array dest_offsets = {
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{current_width > 1 ? current_width / 2 : 1,
|
||||
current_height > 1 ? current_height / 2 : 1, 1}
|
||||
};
|
||||
vk::Offset3D{0, 0, 0}, vk::Offset3D{current_width > 1 ? current_width / 2 : 1,
|
||||
current_height > 1 ? current_height / 2 : 1, 1}};
|
||||
|
||||
const vk::ImageBlit blit_area = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = aspect,
|
||||
const vk::ImageBlit blit_area = {.srcSubresource = {.aspectMask = aspect,
|
||||
.mipLevel = i - 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.srcOffsets = source_offsets,
|
||||
.dstSubresource = {
|
||||
.aspectMask = aspect,
|
||||
.dstSubresource = {.aspectMask = aspect,
|
||||
.mipLevel = i,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.dstOffsets = dest_offsets
|
||||
};
|
||||
.layerCount = 1},
|
||||
.dstOffsets = dest_offsets};
|
||||
|
||||
command_buffer.blitImage(surface.alloc.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
@ -478,13 +443,14 @@ void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
|
||||
}
|
||||
}
|
||||
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(VideoCore::PixelFormat dest_format) const {
|
||||
const ReinterpreterList& TextureRuntime::GetPossibleReinterpretations(
|
||||
VideoCore::PixelFormat dest_format) const {
|
||||
return reinterpreters[static_cast<u32>(dest_format)];
|
||||
}
|
||||
|
||||
void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc,
|
||||
vk::ImageLayout new_layout, u32 level, u32 level_count,
|
||||
u32 layer, u32 layer_count) {
|
||||
vk::ImageLayout new_layout, u32 level, u32 level_count, u32 layer,
|
||||
u32 layer_count) {
|
||||
if (new_layout == alloc.layout || !alloc.image) {
|
||||
return;
|
||||
}
|
||||
@ -515,7 +481,8 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
|
||||
info.stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
break;
|
||||
case vk::ImageLayout::eDepthStencilAttachmentOptimal:
|
||||
// Image was being used as a depthstencil attachment, so ensure all writes have completed.
|
||||
// Image was being used as a depthstencil attachment, so ensure all writes have
|
||||
// completed.
|
||||
info.access = vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite;
|
||||
info.stage = vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
@ -568,18 +535,14 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
|
||||
.oldLayout = alloc.layout,
|
||||
.newLayout = new_layout,
|
||||
.image = alloc.image,
|
||||
.subresourceRange = {
|
||||
.aspectMask = alloc.aspect,
|
||||
.baseMipLevel = /*level*/0,
|
||||
.levelCount = /*level_count*/alloc.levels,
|
||||
.subresourceRange = {.aspectMask = alloc.aspect,
|
||||
.baseMipLevel = /*level*/ 0,
|
||||
.levelCount = /*level_count*/ alloc.levels,
|
||||
.baseArrayLayer = layer,
|
||||
.layerCount = layer_count
|
||||
}
|
||||
};
|
||||
.layerCount = layer_count}};
|
||||
|
||||
command_buffer.pipelineBarrier(source.stage, dest.stage,
|
||||
vk::DependencyFlagBits::eByRegion,
|
||||
{}, {}, barrier);
|
||||
command_buffer.pipelineBarrier(source.stage, dest.stage, vk::DependencyFlagBits::eByRegion, {},
|
||||
{}, barrier);
|
||||
|
||||
alloc.layout = new_layout;
|
||||
}
|
||||
@ -589,7 +552,8 @@ Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
|
||||
scheduler{runtime.GetScheduler()}, traits{instance.GetTraits(pixel_format)} {
|
||||
|
||||
if (pixel_format != VideoCore::PixelFormat::Invalid) {
|
||||
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format, texture_type);
|
||||
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format,
|
||||
texture_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,8 +563,7 @@ Surface::~Surface() {
|
||||
.format = pixel_format,
|
||||
.width = GetScaledWidth(),
|
||||
.height = GetScaledHeight(),
|
||||
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u
|
||||
};
|
||||
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u};
|
||||
|
||||
runtime.Recycle(tag, std::move(alloc));
|
||||
}
|
||||
@ -622,21 +585,18 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa
|
||||
.bufferOffset = staging.buffer_offset,
|
||||
.bufferRowLength = rect.GetWidth(),
|
||||
.bufferImageHeight = rect.GetHeight(),
|
||||
.imageSubresource = {
|
||||
.aspectMask = alloc.aspect,
|
||||
.imageSubresource = {.aspectMask = alloc.aspect,
|
||||
.mipLevel = upload.texture_level,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0},
|
||||
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}
|
||||
};
|
||||
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}};
|
||||
|
||||
runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferDstOptimal, 0, alloc.levels,
|
||||
0, texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferDstOptimal, 0,
|
||||
alloc.levels, 0,
|
||||
texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||
command_buffer.copyBufferToImage(staging.buffer, alloc.image,
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
copy_region);
|
||||
vk::ImageLayout::eTransferDstOptimal, copy_region);
|
||||
}
|
||||
|
||||
InvalidateAllWatcher();
|
||||
@ -665,15 +625,12 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
|
||||
.bufferOffset = staging.buffer_offset + download.buffer_offset,
|
||||
.bufferRowLength = rect.GetWidth(),
|
||||
.bufferImageHeight = rect.GetHeight(),
|
||||
.imageSubresource = {
|
||||
.aspectMask = alloc.aspect,
|
||||
.imageSubresource = {.aspectMask = alloc.aspect,
|
||||
.mipLevel = download.texture_level,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.layerCount = 1},
|
||||
.imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0},
|
||||
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}
|
||||
};
|
||||
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}};
|
||||
|
||||
if (alloc.aspect & vk::ImageAspectFlagBits::eColor) {
|
||||
copy_regions[region_count++] = copy_region;
|
||||
@ -688,7 +645,8 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
|
||||
}
|
||||
}
|
||||
|
||||
runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferSrcOptimal, 0, alloc.levels);
|
||||
runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferSrcOptimal, 0,
|
||||
alloc.levels);
|
||||
|
||||
// Copy pixel data to the staging buffer
|
||||
command_buffer.copyImageToBuffer(alloc.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
@ -703,8 +661,7 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
|
||||
bool Surface::NeedsConvert() const {
|
||||
// RGBA8 needs a byteswap since R8G8B8A8UnormPack32 does not exist
|
||||
// D24S8 always needs an interleave pass even if natively supported
|
||||
return alloc.format != traits.native ||
|
||||
pixel_format == VideoCore::PixelFormat::RGBA8 ||
|
||||
return alloc.format != traits.native || pixel_format == VideoCore::PixelFormat::RGBA8 ||
|
||||
pixel_format == VideoCore::PixelFormat::D24S8;
|
||||
}
|
||||
|
||||
@ -720,7 +677,8 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) {
|
||||
const ImageAlloc unscaled_tex = runtime.Allocate(rect_width, rect_height, pixel_format,
|
||||
VideoCore::TextureType::Texture2D);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type,
|
||||
texture);
|
||||
|
||||
// Blit the scaled rectangle to the unscaled texture
|
||||
const VideoCore::Rect2D scaled_rect = download.texture_rect * res_scale;
|
||||
@ -759,7 +717,8 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload) {
|
||||
const auto& filterer = runtime.GetFilterer();
|
||||
if (!filterer.Filter(unscaled_tex, unscaled_rect, texture, scaled_rect, type)) {
|
||||
runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type, texture);
|
||||
runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type,
|
||||
texture);
|
||||
|
||||
// If filtering fails, resort to normal blitting
|
||||
glBlitFramebuffer(0, 0, rect_width, rect_height,
|
||||
|
@ -3,14 +3,14 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <span>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <vulkan/vulkan_hash.hpp>
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_cache/surface_base.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_format_reinterpreter.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@ -47,6 +47,7 @@ class Surface;
|
||||
*/
|
||||
class TextureRuntime {
|
||||
friend class Surface;
|
||||
|
||||
public:
|
||||
TextureRuntime(const Instance& instance, TaskScheduler& scheduler,
|
||||
RenderpassCache& renderpass_cache);
|
||||
@ -66,13 +67,12 @@ public:
|
||||
void Recycle(const VideoCore::HostTextureTag tag, ImageAlloc&& alloc);
|
||||
|
||||
/// Performs required format convertions on the staging data
|
||||
void FormatConvert(const Surface& surface, bool upload,
|
||||
std::span<std::byte> source, std::span<std::byte> dest);
|
||||
void FormatConvert(const Surface& surface, bool upload, std::span<std::byte> source,
|
||||
std::span<std::byte> dest);
|
||||
|
||||
/// Transitions the mip level range of the surface to new_layout
|
||||
void Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc,
|
||||
vk::ImageLayout new_layout, u32 level, u32 level_count,
|
||||
u32 layer = 0, u32 layer_count = 1);
|
||||
void Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc, vk::ImageLayout new_layout,
|
||||
u32 level, u32 level_count, u32 layer = 0, u32 layer_count = 1);
|
||||
|
||||
/// Fills the rectangle of the texture with the clear value provided
|
||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||
@ -120,6 +120,7 @@ private:
|
||||
class Surface : public VideoCore::SurfaceBase<Surface> {
|
||||
friend class TextureRuntime;
|
||||
friend class RasterizerVulkan;
|
||||
|
||||
public:
|
||||
Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
|
||||
~Surface() override;
|
||||
|
@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include "video_core/shader/shader.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
@ -15,7 +15,7 @@ template <typename ShaderType>
|
||||
using ShaderCacheResult = std::pair<ShaderType, std::optional<std::string>>;
|
||||
|
||||
template <typename KeyType, typename ShaderType, auto ModuleCompiler,
|
||||
std::string(*CodeGenerator)(const KeyType&)>
|
||||
std::string (*CodeGenerator)(const KeyType&)>
|
||||
class ShaderCache {
|
||||
public:
|
||||
ShaderCache() {}
|
||||
@ -52,14 +52,16 @@ public:
|
||||
* different config values from the same shader program.
|
||||
*/
|
||||
template <typename KeyType, typename ShaderType, auto ModuleCompiler,
|
||||
std::optional<std::string>(*CodeGenerator)(const Pica::Shader::ShaderSetup&, const KeyType&)>
|
||||
std::optional<std::string> (*CodeGenerator)(const Pica::Shader::ShaderSetup&,
|
||||
const KeyType&)>
|
||||
class ShaderDoubleCache {
|
||||
public:
|
||||
ShaderDoubleCache() = default;
|
||||
~ShaderDoubleCache() = default;
|
||||
|
||||
template <typename... Args>
|
||||
auto Get(const KeyType& key, const Pica::Shader::ShaderSetup& setup, Args&&... args) -> ShaderCacheResult<ShaderType> {
|
||||
auto Get(const KeyType& key, const Pica::Shader::ShaderSetup& setup, Args&&... args)
|
||||
-> ShaderCacheResult<ShaderType> {
|
||||
if (auto map_iter = shader_map.find(key); map_iter == shader_map.end()) {
|
||||
auto code = CodeGenerator(setup, key);
|
||||
if (!code) {
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs, const Pica::Shader::ShaderSetup& setup) {
|
||||
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
|
||||
const Pica::Shader::ShaderSetup& setup) {
|
||||
std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
|
||||
[](bool value) -> BoolAligned { return {value ? 1 : 0}; });
|
||||
std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
|
||||
|
@ -95,5 +95,4 @@ static_assert(sizeof(VSUniformData) == 1856,
|
||||
static_assert(sizeof(VSUniformData) < 16384,
|
||||
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
@ -71,7 +71,6 @@ void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> des
|
||||
*/
|
||||
void ConvertBGRToRGBA(std::span<const std::byte> source, std::span<std::byte> dest);
|
||||
|
||||
|
||||
/**
|
||||
* Converts pixel data encoded in ABGR format to RGBA
|
||||
*
|
||||
|
@ -47,11 +47,7 @@ extern Layout::FramebufferLayout g_screenshot_framebuffer_layout;
|
||||
|
||||
extern Memory::MemorySystem* g_memory;
|
||||
|
||||
enum class ResultStatus {
|
||||
Success,
|
||||
ErrorRendererInit,
|
||||
ErrorGenericDrivers
|
||||
};
|
||||
enum class ResultStatus { Success, ErrorRendererInit, ErrorGenericDrivers };
|
||||
|
||||
/// Initialize the video core
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window, Memory::MemorySystem& memory);
|
||||
|
@ -3,8 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <system_error>
|
||||
#include "common/logging/log.h"
|
||||
#include <jwt/jwt.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/web_result.h"
|
||||
#include "web_service/verify_user_jwt.h"
|
||||
#include "web_service/web_backend.h"
|
||||
|
Reference in New Issue
Block a user