lightgun crosshair support

This commit is contained in:
flyinghead 2021-01-23 15:59:57 +01:00
parent 4a81430da7
commit 7697d37ec6
18 changed files with 428 additions and 161 deletions

View File

@ -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

View File

@ -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
{

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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());

View File

@ -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));

View File

@ -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);
}
}
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
)";

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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__

View File

@ -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