lightgun crosshair support
This commit is contained in:
parent
4a81430da7
commit
7697d37ec6
|
@ -839,8 +839,8 @@ if(NOT APPLE)
|
|||
core/rend/vulkan/vk_mem_alloc.h
|
||||
core/rend/vulkan/vmallocator.cpp
|
||||
core/rend/vulkan/vmallocator.h
|
||||
core/rend/vulkan/vmu.cpp
|
||||
core/rend/vulkan/vmu.h
|
||||
core/rend/vulkan/overlay.cpp
|
||||
core/rend/vulkan/overlay.h
|
||||
core/rend/vulkan/vulkan_context.cpp
|
||||
core/rend/vulkan/vulkan_context.h
|
||||
core/rend/vulkan/vulkan.h
|
||||
|
|
|
@ -16,7 +16,7 @@ int ConfigEntry::get_int()
|
|||
{
|
||||
if (strstr(this->value.c_str(), "0x") != NULL)
|
||||
{
|
||||
return strtol(this->value.c_str(), NULL, 16);
|
||||
return (int)strtoul(this->value.c_str(), NULL, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -759,6 +759,7 @@ void InitSettings()
|
|||
settings.rend.PerStripSorting = false;
|
||||
settings.rend.DelayFrameSwapping = false;
|
||||
settings.rend.WidescreenGameHacks = false;
|
||||
memset(settings.rend.CrosshairColor, 0, sizeof(settings.rend.CrosshairColor));
|
||||
|
||||
settings.pvr.ta_skip = 0;
|
||||
|
||||
|
@ -860,6 +861,11 @@ void LoadSettings(bool game_specific)
|
|||
settings.rend.PerStripSorting = cfgLoadBool(config_section, "rend.PerStripSorting", settings.rend.PerStripSorting);
|
||||
settings.rend.DelayFrameSwapping = cfgLoadBool(config_section, "rend.DelayFrameSwapping", settings.rend.DelayFrameSwapping);
|
||||
settings.rend.WidescreenGameHacks = cfgLoadBool(config_section, "rend.WidescreenGameHacks", settings.rend.WidescreenGameHacks);
|
||||
for (u32 i = 0; i < ARRAY_SIZE(settings.rend.CrosshairColor); i++)
|
||||
{
|
||||
std::string name = "rend.CrossHairColor" + std::to_string(i + 1);
|
||||
settings.rend.CrosshairColor[i] = cfgLoadInt(config_section, name.c_str(), settings.rend.CrosshairColor[i]);
|
||||
}
|
||||
|
||||
settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip);
|
||||
|
||||
|
@ -1029,6 +1035,11 @@ void SaveSettings()
|
|||
cfgSaveBool("config", "rend.PerStripSorting", settings.rend.PerStripSorting);
|
||||
cfgSaveBool("config", "rend.DelayFrameSwapping", settings.rend.DelayFrameSwapping);
|
||||
cfgSaveBool("config", "rend.WidescreenGameHacks", settings.rend.WidescreenGameHacks);
|
||||
for (u32 i = 0; i < ARRAY_SIZE(settings.rend.CrosshairColor); i++)
|
||||
{
|
||||
std::string name = "rend.CrossHairColor" + std::to_string(i + 1);
|
||||
cfgSaveInt("config", name.c_str(), settings.rend.CrosshairColor[i]);
|
||||
}
|
||||
|
||||
cfgSaveInt("config", "pvr.MaxThreads", settings.pvr.MaxThreads);
|
||||
cfgSaveInt("config", "pvr.AutoSkipFrame", settings.pvr.AutoSkipFrame);
|
||||
|
|
|
@ -528,18 +528,29 @@ void ImGui_ImplOpenGL3_DrawBackground()
|
|||
}
|
||||
}
|
||||
|
||||
ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data)
|
||||
static ImTextureID createSimpleTexture(const unsigned int *data, u32 width, u32 height)
|
||||
{
|
||||
GLuint tex_id = glcache.GenTexture();
|
||||
glcache.BindTexture(GL_TEXTURE_2D, tex_id);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 48, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
|
||||
return reinterpret_cast<ImTextureID>(tex_id);
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID tex_id)
|
||||
ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data)
|
||||
{
|
||||
return createSimpleTexture(data, 48, 32);
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DeleteTexture(ImTextureID tex_id)
|
||||
{
|
||||
glcache.DeleteTextures(1, &(GLuint &)tex_id);
|
||||
}
|
||||
|
||||
ImTextureID ImGui_ImplOpenGL3_CreateCrosshairTexture(const unsigned int *data)
|
||||
{
|
||||
return createSimpleTexture(data, 16, 16);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
|||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_background = false);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DrawBackground();
|
||||
IMGUI_IMPL_API ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DeleteTexture(ImTextureID);
|
||||
ImTextureID ImGui_ImplOpenGL3_CreateCrosshairTexture(const unsigned int *data);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
|
|
|
@ -66,6 +66,7 @@ static std::mutex osd_message_mutex;
|
|||
static void display_vmus();
|
||||
static void reset_vmus();
|
||||
static void term_vmus();
|
||||
static void displayCrosshairs();
|
||||
|
||||
GameScanner scanner;
|
||||
|
||||
|
@ -1031,6 +1032,38 @@ static void gui_display_settings()
|
|||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (settings.input.maple_devices[bus] == MDT_LightGun)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
sprintf(device_name, "##device%d.xhair", bus);
|
||||
ImGui::PushID(device_name);
|
||||
u32 color = settings.rend.CrosshairColor[bus];
|
||||
float xhairColor[4] {
|
||||
(color & 0xff) / 255.f,
|
||||
((color >> 8) & 0xff) / 255.f,
|
||||
((color >> 16) & 0xff) / 255.f,
|
||||
((color >> 24) & 0xff) / 255.f
|
||||
};
|
||||
ImGui::ColorEdit4("Crosshair color", xhairColor, ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf
|
||||
| ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoLabel);
|
||||
ImGui::SameLine();
|
||||
bool enabled = color != 0;
|
||||
ImGui::Checkbox("Crosshair", &enabled);
|
||||
if (enabled)
|
||||
{
|
||||
settings.rend.CrosshairColor[bus] = (u8)(xhairColor[0] * 255.f)
|
||||
| ((u8)(xhairColor[1] * 255.f) << 8)
|
||||
| ((u8)(xhairColor[2] * 255.f) << 16)
|
||||
| ((u8)(xhairColor[3] * 255.f) << 24);
|
||||
if (settings.rend.CrosshairColor[bus] == 0)
|
||||
settings.rend.CrosshairColor[bus] = 0xC0FFFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.rend.CrosshairColor[bus] = 0;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
|
@ -1989,6 +2022,7 @@ void gui_display_osd()
|
|||
ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", message.c_str());
|
||||
ImGui::End();
|
||||
}
|
||||
displayCrosshairs();
|
||||
if (settings.rend.FloatVMUs)
|
||||
display_vmus();
|
||||
// gui_plot_render_time(screen_width, screen_height);
|
||||
|
@ -2046,6 +2080,8 @@ bool vmu_lcd_status[8];
|
|||
bool vmu_lcd_changed[8];
|
||||
static ImTextureID vmu_lcd_tex_ids[8];
|
||||
|
||||
static ImTextureID crosshairTexId;
|
||||
|
||||
void push_vmu_screen(int bus_id, int bus_port, u8* buffer)
|
||||
{
|
||||
int vmu_id = bus_id * 2 + bus_port;
|
||||
|
@ -2087,7 +2123,7 @@ static void display_vmus()
|
|||
continue;
|
||||
|
||||
if (vmu_lcd_tex_ids[i] != (ImTextureID)0)
|
||||
ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]);
|
||||
ImGui_ImplOpenGL3_DeleteTexture(vmu_lcd_tex_ids[i]);
|
||||
vmu_lcd_tex_ids[i] = ImGui_ImplOpenGL3_CreateVmuTexture(vmu_lcd_data[i]);
|
||||
|
||||
int x = vmu_coords[i][0];
|
||||
|
@ -2115,6 +2151,96 @@ static void display_vmus()
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
static const int lightgunCrosshairData[16 * 16] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
-1,-1,-1,-1,-1,-1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
const u32 *getCrosshairTextureData()
|
||||
{
|
||||
return (u32 *)lightgunCrosshairData;
|
||||
}
|
||||
|
||||
std::pair<float, float> getCrosshairPosition(int playerNum)
|
||||
{
|
||||
float fx = mo_x_abs[playerNum];
|
||||
float fy = mo_y_abs[playerNum];
|
||||
int width = screen_width;
|
||||
int height = screen_height;
|
||||
if (settings.rend.Rotate90)
|
||||
{
|
||||
float t = fy;
|
||||
fy = width - fx;
|
||||
fx = t;
|
||||
std::swap(width, height);
|
||||
}
|
||||
if ((float)width / height >= 640.f / 480.f)
|
||||
{
|
||||
float scale = 480.f / height;
|
||||
fy /= scale;
|
||||
scale *= settings.rend.ScreenStretching / 100.f;
|
||||
fx = fx / scale + (width - 640.f / scale) / 2.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
float scale = 640.f / width;
|
||||
fx /= scale;
|
||||
scale *= settings.rend.ScreenStretching / 100.f;
|
||||
fy = fy / scale + (height - 480.f / scale) / 2.f;
|
||||
}
|
||||
return std::make_pair(fx, fy);
|
||||
}
|
||||
|
||||
static void displayCrosshairs()
|
||||
{
|
||||
if (!game_started)
|
||||
return;
|
||||
if (!settings.pvr.IsOpenGL())
|
||||
return;
|
||||
if (!crosshairsNeeded())
|
||||
return;
|
||||
|
||||
if (crosshairTexId == ImTextureID())
|
||||
crosshairTexId = ImGui_ImplOpenGL3_CreateCrosshairTexture(getCrosshairTextureData());
|
||||
ImGui::SetNextWindowBgAlpha(0);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height));
|
||||
|
||||
ImGui::Begin("xhair-window", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoInputs
|
||||
| ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
for (u32 i = 0; i < ARRAY_SIZE(settings.rend.CrosshairColor); i++)
|
||||
{
|
||||
if (settings.rend.CrosshairColor[i] == 0)
|
||||
continue;
|
||||
if (settings.platform.system == DC_PLATFORM_DREAMCAST && settings.input.maple_devices[i] != MDT_LightGun)
|
||||
continue;
|
||||
|
||||
ImVec2 pos;
|
||||
std::tie(pos.x, pos.y) = getCrosshairPosition(i);
|
||||
pos.x -= XHAIR_WIDTH / 2.f;
|
||||
pos.y += XHAIR_WIDTH / 2.f;
|
||||
ImVec2 pos_b(pos.x + XHAIR_WIDTH, pos.y - XHAIR_HEIGHT);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(crosshairTexId, pos, pos_b, ImVec2(0, 1), ImVec2(1, 0), settings.rend.CrosshairColor[i]);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
static void reset_vmus()
|
||||
{
|
||||
for (u32 i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++)
|
||||
|
@ -2127,10 +2253,15 @@ static void term_vmus()
|
|||
return;
|
||||
for (u32 i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++)
|
||||
{
|
||||
if (vmu_lcd_tex_ids[i] != (ImTextureID)0)
|
||||
if (vmu_lcd_tex_ids[i] != ImTextureID())
|
||||
{
|
||||
ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]);
|
||||
vmu_lcd_tex_ids[i] = (ImTextureID)0;
|
||||
ImGui_ImplOpenGL3_DeleteTexture(vmu_lcd_tex_ids[i]);
|
||||
vmu_lcd_tex_ids[i] = ImTextureID();
|
||||
}
|
||||
}
|
||||
if (crosshairTexId != ImTextureID())
|
||||
{
|
||||
ImGui_ImplOpenGL3_DeleteTexture(crosshairTexId);
|
||||
crosshairTexId = ImTextureID();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,3 +57,21 @@ static inline bool gui_is_content_browser()
|
|||
return gui_state == Main;
|
||||
}
|
||||
float gui_get_scaling();
|
||||
|
||||
#define XHAIR_WIDTH (40 * scaling)
|
||||
#define XHAIR_HEIGHT (40 * scaling)
|
||||
static inline bool crosshairsNeeded()
|
||||
{
|
||||
if (settings.rend.CrosshairColor[0] == 0 && settings.rend.CrosshairColor[1] == 0
|
||||
&& settings.rend.CrosshairColor[3] == 0 && settings.rend.CrosshairColor[3] == 0)
|
||||
return false;
|
||||
if (settings.platform.system != DC_PLATFORM_DREAMCAST
|
||||
&& settings.input.JammaSetup != JVS::LightGun
|
||||
&& settings.input.JammaSetup != JVS::LightGunAsAnalog
|
||||
&& settings.input.JammaSetup != JVS::Mazan)
|
||||
// not a lightgun game
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
const u32 *getCrosshairTextureData();
|
||||
std::pair<float, float> getCrosshairPosition(int playerNum);
|
||||
|
|
|
@ -45,10 +45,10 @@ static inline void ImGui_impl_RenderDrawData(ImDrawData *draw_data, bool save_ba
|
|||
if (!rendering)
|
||||
{
|
||||
context->NewFrame();
|
||||
vmuCmdBuffers = context->PrepareVMUs();
|
||||
vmuCmdBuffers = context->PrepareOverlay(true, false);
|
||||
context->BeginRenderPass();
|
||||
context->PresentLastFrame();
|
||||
context->DrawVMUs(gui_get_scaling());
|
||||
context->DrawOverlay(gui_get_scaling(), true, false);
|
||||
}
|
||||
// Record Imgui Draw Data and draw funcs into command buffer
|
||||
ImGui_ImplVulkan_RenderDrawData(draw_data, (VkCommandBuffer)context->GetCurrentCommandBuffer());
|
||||
|
|
|
@ -40,7 +40,7 @@ struct BufferData
|
|||
memcpy(dataPtr, data, size);
|
||||
}
|
||||
|
||||
void upload(size_t count, u32 *sizes, const void **data, u32 bufOffset = 0) const
|
||||
void upload(size_t count, const u32 *sizes, const void * const *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Created on: Dec 13, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "texture.h"
|
||||
#include "rend/gui.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "overlay.h"
|
||||
|
||||
VulkanOverlay::~VulkanOverlay()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> VulkanOverlay::createTexture(vk::CommandPool commandPool, int width, int height, u8 *data)
|
||||
{
|
||||
VulkanContext *context = VulkanContext::Instance();
|
||||
auto texture = std::unique_ptr<Texture>(new Texture());
|
||||
texture->tex_type = TextureType::_8888;
|
||||
texture->SetDevice(context->GetDevice());
|
||||
texture->SetPhysicalDevice(context->GetPhysicalDevice());
|
||||
commandBuffers[context->GetCurrentImageIndex()].emplace_back(std::move(
|
||||
VulkanContext::Instance()->GetDevice().allocateCommandBuffersUnique(vk::CommandBufferAllocateInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1))
|
||||
.front()));
|
||||
texture->SetCommandBuffer(*commandBuffers[context->GetCurrentImageIndex()].back());
|
||||
texture->UploadToGPU(width, height, data, false);
|
||||
texture->SetCommandBuffer(nullptr);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
const std::vector<vk::UniqueCommandBuffer>* VulkanOverlay::Prepare(vk::CommandPool commandPool, bool vmu, bool crosshair)
|
||||
{
|
||||
VulkanContext *context = VulkanContext::Instance();
|
||||
commandBuffers.resize(context->GetSwapChainSize());
|
||||
commandBuffers[context->GetCurrentImageIndex()].clear();
|
||||
if (vmu)
|
||||
{
|
||||
for (size_t i = 0; i < vmuTextures.size(); i++)
|
||||
{
|
||||
std::unique_ptr<Texture>& texture = vmuTextures[i];
|
||||
if (!vmu_lcd_status[i])
|
||||
{
|
||||
texture.reset();
|
||||
continue;
|
||||
}
|
||||
if (texture != nullptr && !vmu_lcd_changed[i])
|
||||
continue;
|
||||
|
||||
texture = createTexture(commandPool, 48, 32, (u8*)vmu_lcd_data[i]);
|
||||
vmu_lcd_changed[i] = false;
|
||||
}
|
||||
}
|
||||
if (crosshair && !xhairTexture)
|
||||
{
|
||||
const u32* texData = getCrosshairTextureData();
|
||||
xhairTexture = createTexture(commandPool, 16, 16, (u8*)texData);
|
||||
// delete [] texData;
|
||||
}
|
||||
|
||||
return &commandBuffers[context->GetCurrentImageIndex()];
|
||||
}
|
||||
|
||||
void VulkanOverlay::Draw(vk::Extent2D viewport, float scaling, bool vmu, bool crosshair)
|
||||
{
|
||||
VulkanContext *context = VulkanContext::Instance();
|
||||
vk::CommandBuffer commandBuffer = context->GetCurrentCommandBuffer();
|
||||
QuadVertex vtx[] = {
|
||||
{ { -1.f, -1.f, 0.f }, { 0.f, 1.f } },
|
||||
{ { 1.f, -1.f, 0.f }, { 1.f, 1.f } },
|
||||
{ { -1.f, 1.f, 0.f }, { 0.f, 0.f } },
|
||||
{ { 1.f, 1.f, 0.f }, { 1.f, 0.f } },
|
||||
};
|
||||
|
||||
if (vmu)
|
||||
{
|
||||
f32 vmu_padding = 8.f * scaling;
|
||||
f32 vmu_height = 70.f * scaling;
|
||||
f32 vmu_width = 48.f / 32.f * vmu_height;
|
||||
|
||||
pipeline->BindPipeline(commandBuffer);
|
||||
float blendConstants[4] = { 0.75f, 0.75f, 0.75f, 0.75f };
|
||||
commandBuffer.setBlendConstants(blendConstants);
|
||||
|
||||
for (size_t i = 0; i < vmuTextures.size(); i++)
|
||||
{
|
||||
if (!vmuTextures[i])
|
||||
continue;
|
||||
f32 x;
|
||||
if (i & 2)
|
||||
x = viewport.width - vmu_padding - vmu_width;
|
||||
else
|
||||
x = vmu_padding;
|
||||
f32 y;
|
||||
if (i & 4)
|
||||
{
|
||||
y = viewport.height - vmu_padding - vmu_height;
|
||||
if (i & 1)
|
||||
y -= vmu_padding + vmu_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = vmu_padding;
|
||||
if (i & 1)
|
||||
y += vmu_padding + vmu_height;
|
||||
}
|
||||
vk::Viewport viewport(x, y, vmu_width, vmu_height);
|
||||
commandBuffer.setViewport(0, 1, &viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(vmu_width, vmu_height)));
|
||||
|
||||
drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true);
|
||||
}
|
||||
}
|
||||
if (crosshair && crosshairsNeeded())
|
||||
{
|
||||
alphaPipeline->BindPipeline(commandBuffer);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(settings.rend.CrosshairColor); i++)
|
||||
{
|
||||
if (settings.rend.CrosshairColor[i] == 0)
|
||||
continue;
|
||||
if (settings.platform.system == DC_PLATFORM_DREAMCAST && settings.input.maple_devices[i] != MDT_LightGun)
|
||||
continue;
|
||||
|
||||
float x, y;
|
||||
std::tie(x, y) = getCrosshairPosition(i);
|
||||
x -= XHAIR_WIDTH / 2;
|
||||
y -= XHAIR_HEIGHT / 2;
|
||||
vk::Viewport viewport(x, y, XHAIR_WIDTH, XHAIR_HEIGHT);
|
||||
commandBuffer.setViewport(0, 1, &viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(XHAIR_WIDTH, XHAIR_HEIGHT)));
|
||||
u32 color = settings.rend.CrosshairColor[i];
|
||||
float xhairColor[4] {
|
||||
(color & 0xff) / 255.f,
|
||||
((color >> 8) & 0xff) / 255.f,
|
||||
((color >> 16) & 0xff) / 255.f,
|
||||
((color >> 24) & 0xff) / 255.f
|
||||
};
|
||||
xhairDrawer->Draw(commandBuffer, xhairTexture->GetImageView(), vtx, true, xhairColor);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,24 +27,36 @@
|
|||
|
||||
class Texture;
|
||||
|
||||
class VulkanVMUs
|
||||
class VulkanOverlay
|
||||
{
|
||||
public:
|
||||
~VulkanVMUs();
|
||||
void Init(QuadPipeline *pipeline) {
|
||||
~VulkanOverlay();
|
||||
|
||||
void Init(QuadPipeline *pipeline)
|
||||
{
|
||||
this->pipeline = pipeline;
|
||||
alphaPipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(true));
|
||||
alphaPipeline->Init(*pipeline);
|
||||
for (auto& drawer : drawers)
|
||||
{
|
||||
drawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
drawer->Init(pipeline);
|
||||
}
|
||||
xhairDrawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
xhairDrawer->Init(alphaPipeline.get());
|
||||
}
|
||||
const std::vector<vk::UniqueCommandBuffer>* PrepareVMUs(vk::CommandPool commandPool);
|
||||
void DrawVMUs(vk::Extent2D viewport, float scaling);
|
||||
const std::vector<vk::UniqueCommandBuffer>* Prepare(vk::CommandPool commandPool, bool vmu, bool crosshair);
|
||||
void Draw(vk::Extent2D viewport, float scaling, bool vmu, bool crosshair);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Texture> createTexture(vk::CommandPool commandPool, int width, int height, u8 *data);
|
||||
|
||||
std::array<std::unique_ptr<Texture>, 8> vmuTextures;
|
||||
std::vector<std::vector<vk::UniqueCommandBuffer>> commandBuffers;
|
||||
std::array<std::unique_ptr<QuadDrawer>, 8> drawers;
|
||||
QuadPipeline *pipeline = nullptr;
|
||||
|
||||
std::unique_ptr<QuadPipeline> alphaPipeline;
|
||||
std::unique_ptr<Texture> xhairTexture;
|
||||
std::unique_ptr<QuadDrawer> xhairDrawer;
|
||||
};
|
|
@ -65,17 +65,35 @@ void QuadPipeline::CreatePipeline()
|
|||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
if (withAlpha)
|
||||
{
|
||||
pipelineColorBlendAttachmentState = vk::PipelineColorBlendAttachmentState(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eSrcAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eSrcAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
pipelineColorBlendAttachmentState = vk::PipelineColorBlendAttachmentState(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
}
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
|
@ -87,7 +105,7 @@ void QuadPipeline::CreatePipeline()
|
|||
);
|
||||
|
||||
vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eBlendConstants };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates),
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates) - (withAlpha ? 1 : 0),
|
||||
dynamicStates);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
|
@ -125,8 +143,9 @@ void QuadPipeline::Init(ShaderManager *shaderManager, vk::RenderPass renderPass)
|
|||
};
|
||||
descSetLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(bindings), bindings));
|
||||
vk::PushConstantRange pushConstant(vk::ShaderStageFlagBits::eFragment, 0, 4 * sizeof(float));
|
||||
pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique(
|
||||
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get()));
|
||||
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get(), 1, &pushConstant));
|
||||
}
|
||||
if (!linearSampler)
|
||||
{
|
||||
|
@ -168,7 +187,7 @@ void QuadDrawer::Init(QuadPipeline *pipeline)
|
|||
descSet.reset();
|
||||
}
|
||||
|
||||
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[], bool nearestFilter)
|
||||
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[], bool nearestFilter, const float *color)
|
||||
{
|
||||
VulkanContext *context = GetContext();
|
||||
auto &descSet = descriptorSets[context->GetCurrentImageIndex()];
|
||||
|
@ -187,5 +206,12 @@ void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView,
|
|||
|
||||
buffer->Update(vertices);
|
||||
buffer->Bind(commandBuffer);
|
||||
|
||||
if (color == nullptr)
|
||||
{
|
||||
static float fullWhite[] { 1.f, 1.f, 1.f, 1.f };
|
||||
color = fullWhite;
|
||||
}
|
||||
commandBuffer.pushConstants(pipeline->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, sizeof(float) * 4, color);
|
||||
buffer->Draw(commandBuffer);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,9 @@ private:
|
|||
class QuadPipeline
|
||||
{
|
||||
public:
|
||||
QuadPipeline(bool withAlpha = false) : withAlpha(withAlpha) {}
|
||||
void Init(ShaderManager *shaderManager, vk::RenderPass renderPass);
|
||||
void Init(const QuadPipeline& other) { Init(other.shaderManager, other.renderPass); }
|
||||
void Term() {
|
||||
pipeline.reset();
|
||||
linearSampler.reset();
|
||||
|
@ -99,7 +101,8 @@ private:
|
|||
vk::UniqueSampler nearestSampler;
|
||||
vk::UniquePipelineLayout pipelineLayout;
|
||||
vk::UniqueDescriptorSetLayout descSetLayout;
|
||||
ShaderManager *shaderManager;
|
||||
ShaderManager *shaderManager = nullptr;
|
||||
bool withAlpha;
|
||||
};
|
||||
|
||||
class QuadDrawer
|
||||
|
@ -112,7 +115,7 @@ public:
|
|||
QuadDrawer& operator=(const QuadDrawer &) = delete;
|
||||
|
||||
void Init(QuadPipeline *pipeline);
|
||||
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[] = nullptr, bool nearestFilter = false);
|
||||
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[] = nullptr, bool nearestFilter = false, const float *color = nullptr);
|
||||
private:
|
||||
QuadPipeline *pipeline = nullptr;
|
||||
std::unique_ptr<QuadBuffer> buffer;
|
||||
|
|
|
@ -304,13 +304,19 @@ void main()
|
|||
|
||||
static const char QuadFragmentShaderSource[] = R"(#version 450
|
||||
|
||||
layout (binding = 0) uniform sampler2D tex;
|
||||
layout (set = 0, binding = 0) uniform sampler2D tex;
|
||||
|
||||
layout (push_constant) uniform pushBlock
|
||||
{
|
||||
vec4 color;
|
||||
} pushConstants;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = texture(tex, inUV);
|
||||
FragColor = pushConstants.color * texture(tex, inUV);
|
||||
}
|
||||
)";
|
||||
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
Created on: Dec 13, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "vmu.h"
|
||||
#include "texture.h"
|
||||
#include "rend/gui.h"
|
||||
|
||||
VulkanVMUs::~VulkanVMUs()
|
||||
{
|
||||
}
|
||||
|
||||
const std::vector<vk::UniqueCommandBuffer>* VulkanVMUs::PrepareVMUs(vk::CommandPool commandPool)
|
||||
{
|
||||
VulkanContext *context = VulkanContext::Instance();
|
||||
commandBuffers.resize(context->GetSwapChainSize());
|
||||
commandBuffers[context->GetCurrentImageIndex()].clear();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
std::unique_ptr<Texture>& texture = vmuTextures[i];
|
||||
if (!vmu_lcd_status[i])
|
||||
{
|
||||
texture.reset();
|
||||
continue;
|
||||
}
|
||||
if (!texture)
|
||||
texture = std::unique_ptr<Texture>(new Texture());
|
||||
else if (!vmu_lcd_changed[i])
|
||||
continue;
|
||||
|
||||
texture->tex_type = TextureType::_8888;
|
||||
texture->SetDevice(context->GetDevice());
|
||||
texture->SetPhysicalDevice(context->GetPhysicalDevice());
|
||||
commandBuffers[context->GetCurrentImageIndex()].emplace_back(std::move(
|
||||
VulkanContext::Instance()->GetDevice().allocateCommandBuffersUnique(vk::CommandBufferAllocateInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1))
|
||||
.front()));
|
||||
texture->SetCommandBuffer(*commandBuffers[context->GetCurrentImageIndex()].back());
|
||||
texture->UploadToGPU(48, 32, (u8*)vmu_lcd_data[i], false);
|
||||
texture->SetCommandBuffer(nullptr);
|
||||
vmu_lcd_changed[i] = false;
|
||||
}
|
||||
return &commandBuffers[context->GetCurrentImageIndex()];
|
||||
}
|
||||
|
||||
void VulkanVMUs::DrawVMUs(vk::Extent2D viewport, float scaling)
|
||||
{
|
||||
f32 vmu_padding = 8.f * scaling;
|
||||
f32 vmu_height = 70.f * scaling;
|
||||
f32 vmu_width = 48.f / 32.f * vmu_height;
|
||||
|
||||
VulkanContext *context = VulkanContext::Instance();
|
||||
vk::CommandBuffer commandBuffer = context->GetCurrentCommandBuffer();
|
||||
pipeline->BindPipeline(commandBuffer);
|
||||
float blendConstants[4] = { 0.75f, 0.75f, 0.75f, 0.75f };
|
||||
commandBuffer.setBlendConstants(blendConstants);
|
||||
QuadVertex vtx[] = {
|
||||
{ { -1.f, -1.f, 0.f }, { 0.f, 1.f } },
|
||||
{ { 1.f, -1.f, 0.f }, { 1.f, 1.f } },
|
||||
{ { -1.f, 1.f, 0.f }, { 0.f, 0.f } },
|
||||
{ { 1.f, 1.f, 0.f }, { 1.f, 0.f } },
|
||||
};
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (!vmuTextures[i])
|
||||
continue;
|
||||
f32 x;
|
||||
if (i & 2)
|
||||
x = viewport.width - vmu_padding - vmu_width;
|
||||
else
|
||||
x = vmu_padding;
|
||||
f32 y;
|
||||
if (i & 4)
|
||||
{
|
||||
y = viewport.height - vmu_padding - vmu_height;
|
||||
if (i & 1)
|
||||
y -= vmu_padding + vmu_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = vmu_padding;
|
||||
if (i & 1)
|
||||
y += vmu_padding + vmu_height;
|
||||
}
|
||||
vk::Viewport viewport(x, y, vmu_width, vmu_height);
|
||||
commandBuffer.setViewport(0, 1, &viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(vmu_width, vmu_height)));
|
||||
|
||||
drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true);
|
||||
}
|
||||
}
|
|
@ -670,7 +670,7 @@ void VulkanContext::CreateSwapChain()
|
|||
}
|
||||
quadPipeline->Init(shaderManager.get(), *renderPass);
|
||||
quadDrawer->Init(quadPipeline.get());
|
||||
vmus->Init(quadPipeline.get());
|
||||
overlay->Init(quadPipeline.get());
|
||||
|
||||
InitImgui();
|
||||
|
||||
|
@ -726,7 +726,7 @@ bool VulkanContext::Init()
|
|||
vk::AndroidSurfaceCreateInfoKHR createInfo(vk::AndroidSurfaceCreateFlagsKHR(), (struct ANativeWindow*)window);
|
||||
surface = instance->createAndroidSurfaceKHRUnique(createInfo);
|
||||
#endif
|
||||
vmus = std::unique_ptr<VulkanVMUs>(new VulkanVMUs());
|
||||
overlay = std::unique_ptr<VulkanOverlay>(new VulkanOverlay());
|
||||
|
||||
return InitDevice();
|
||||
}
|
||||
|
@ -850,15 +850,15 @@ std::string VulkanContext::GetDriverVersion() const
|
|||
+ std::to_string(VK_VERSION_PATCH(props.driverVersion));
|
||||
}
|
||||
|
||||
const std::vector<vk::UniqueCommandBuffer> *VulkanContext::PrepareVMUs()
|
||||
const std::vector<vk::UniqueCommandBuffer> *VulkanContext::PrepareOverlay(bool vmu, bool crosshair)
|
||||
{
|
||||
return vmus->PrepareVMUs(*commandPools[GetCurrentImageIndex()]);
|
||||
return overlay->Prepare(*commandPools[GetCurrentImageIndex()], vmu, crosshair);
|
||||
}
|
||||
|
||||
void VulkanContext::DrawVMUs(float scaling)
|
||||
void VulkanContext::DrawOverlay(float scaling, bool vmu, bool crosshair)
|
||||
{
|
||||
if (IsValid())
|
||||
vmus->DrawVMUs(vk::Extent2D(width, height), scaling);
|
||||
overlay->Draw(vk::Extent2D(width, height), scaling, vmu, crosshair);
|
||||
}
|
||||
|
||||
extern Renderer *renderer;
|
||||
|
@ -872,19 +872,16 @@ void VulkanContext::PresentFrame(vk::ImageView imageView, vk::Extent2D extent) n
|
|||
{
|
||||
try {
|
||||
NewFrame();
|
||||
const std::vector<vk::UniqueCommandBuffer> *vmuCmdBuffers = nullptr;
|
||||
if (settings.rend.FloatVMUs)
|
||||
vmuCmdBuffers = PrepareVMUs();
|
||||
auto overlayCmdBuffers = PrepareOverlay(settings.rend.FloatVMUs, true);
|
||||
|
||||
BeginRenderPass();
|
||||
|
||||
if (lastFrameView) // Might have been nullified if swap chain recreated
|
||||
DrawFrame(imageView, extent);
|
||||
|
||||
if (settings.rend.FloatVMUs)
|
||||
DrawVMUs(gui_get_scaling());
|
||||
DrawOverlay(gui_get_scaling(), settings.rend.FloatVMUs, true);
|
||||
renderer->DrawOSD(false);
|
||||
EndFrame(vmuCmdBuffers);
|
||||
EndFrame(overlayCmdBuffers);
|
||||
} catch (const InvalidVulkanContext& err) {
|
||||
}
|
||||
}
|
||||
|
@ -918,7 +915,7 @@ void VulkanContext::Term()
|
|||
}
|
||||
}
|
||||
}
|
||||
vmus.reset();
|
||||
overlay.reset();
|
||||
ShaderCompiler::Term();
|
||||
swapChain.reset();
|
||||
imageViews.clear();
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
#include <stdexcept>
|
||||
#include "vulkan.h"
|
||||
#include "vmallocator.h"
|
||||
#include "quad.h"
|
||||
#include "rend/TexCache.h"
|
||||
#include "vmu.h"
|
||||
#include "overlay.h"
|
||||
|
||||
extern int screen_width, screen_height;
|
||||
|
||||
|
@ -97,8 +98,8 @@ public:
|
|||
u32 GetMaxStorageBufferRange() const { return maxStorageBufferRange; }
|
||||
vk::DeviceSize GetMaxMemoryAllocationSize() const { return maxMemoryAllocationSize; }
|
||||
u32 GetVendorID() const { return vendorID; }
|
||||
const std::vector<vk::UniqueCommandBuffer> *PrepareVMUs();
|
||||
void DrawVMUs(float scaling);
|
||||
const std::vector<vk::UniqueCommandBuffer> *PrepareOverlay(bool vmu, bool crosshair);
|
||||
void DrawOverlay(float scaling, bool vmu, bool crosshair);
|
||||
|
||||
#ifdef VK_DEBUG
|
||||
void setObjectName(u64 object, VkDebugReportObjectTypeEXT objectType, const std::string& name)
|
||||
|
@ -186,7 +187,7 @@ private:
|
|||
vk::ImageView lastFrameView;
|
||||
vk::Extent2D lastFrameExtent;
|
||||
|
||||
std::unique_ptr<VulkanVMUs> vmus;
|
||||
std::unique_ptr<VulkanOverlay> overlay;
|
||||
|
||||
#ifdef VK_DEBUG
|
||||
#ifndef __ANDROID__
|
||||
|
|
|
@ -346,6 +346,7 @@ struct settings_t
|
|||
bool PerStripSorting;
|
||||
bool DelayFrameSwapping; // Delay swapping frame until FB_R_SOF matches FB_W_SOF
|
||||
bool WidescreenGameHacks;
|
||||
u32 CrosshairColor[4];
|
||||
} rend;
|
||||
|
||||
struct
|
||||
|
|
Loading…
Reference in New Issue