Files
LibretroAdmin b75c3362e7 Vulkan: make the VMU and crosshair pictures in the upload ring
Their pixels are written into the texture's piece of the upload ring, which the upload then takes as it is, instead of on the stack and copied there.
2026-10-28 08:23:04 +01:00

225 lines
6.9 KiB
C++

#define PVR_REGS_FOR_RENDERER // see hw/pvr/pvr_regs.h
/*
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"
VulkanOSD::~VulkanOSD()
{
}
const std::vector<vk::CommandBuffer>* VulkanOSD::PrepareOSD(CommandPool *commandPool)
{
VulkanContext *context = VulkanContext::Instance();
const size_t imageIndex = context->GetCurrentImageIndex();
if (commandBuffers.size() <= imageIndex)
commandBuffers.resize(std::max((size_t)context->GetSwapChainSize(), imageIndex + 1));
commandBuffers[imageIndex].clear();
for (int i = 0; i < VMU_COUNT; i++)
{
std::unique_ptr<Texture>& texture = vmuTextures[i];
auto& vmu_screen_param = vmu_screen_params[i];
// A new picture from the game, if there is one
const bool lcd_changed = vmu_lcd_refresh(i);
if (!vmu_screen_param.vmu_screen_display || vmu_lcd_pixels(i) == nullptr)
{
texture.reset();
continue;
}
if (!texture)
texture = std::unique_ptr<Texture>(new Texture());
else if (!lcd_changed && !vmu_screen_param.vmu_screen_needs_update)
continue;
texture->tex_type = TextureType::_8888;
texture->SetDevice(context->GetDevice());
texture->SetPhysicalDevice(context->GetPhysicalDevice());
/* (made straight in the upload ring's piece for it, which the
* upload then takes as it is; on the stack if it has no room) */
u8 temp_tex_buffer[VMU_SCREEN_HEIGHT * VMU_SCREEN_WIDTH * 4];
u8 *pixels = (u8 *)texture->UploadMemory(VMU_SCREEN_WIDTH, VMU_SCREEN_HEIGHT, sizeof(temp_tex_buffer), 4, false);
if (pixels == nullptr)
pixels = temp_tex_buffer;
u8 *dst = pixels;
const u8 *src = vmu_lcd_pixels(i);
for (int y = 0; y < VMU_SCREEN_HEIGHT; y++)
{
for (int x = 0; x < VMU_SCREEN_WIDTH; x++)
{
if (*src++)
{
*dst++ = vmu_screen_param.vmu_pixel_on_R;
*dst++ = vmu_screen_param.vmu_pixel_on_G;
*dst++ = vmu_screen_param.vmu_pixel_on_B;
}
else
{
*dst++ = vmu_screen_param.vmu_pixel_off_R;
*dst++ = vmu_screen_param.vmu_pixel_off_G;
*dst++ = vmu_screen_param.vmu_pixel_off_B;
}
*dst++ = 0xff;
}
}
commandBuffers[context->GetCurrentImageIndex()].push_back(commandPool->Allocate());
texture->SetCommandBuffer(commandBuffers[context->GetCurrentImageIndex()].back());
texture->UploadToGPU(VMU_SCREEN_WIDTH, VMU_SCREEN_HEIGHT, pixels, false);
texture->SetCommandBuffer(nullptr);
vmu_screen_param.vmu_screen_needs_update = false;
}
for (int i = 0; i < XHAIR_COUNT; i++)
{
std::unique_ptr<Texture>& texture = xhairTextures[i];
if (lightgun_params[i].offscreen || lightgun_params[i].colour == 0)
{
texture.reset();
continue;
}
if (!texture)
texture = std::unique_ptr<Texture>(new Texture());
else if (!lightgun_params[i].dirty)
continue;
texture->tex_type = TextureType::_8888;
texture->SetDevice(context->GetDevice());
texture->SetPhysicalDevice(context->GetPhysicalDevice());
// (as the VMUs')
u8 temp_tex_buffer[LIGHTGUN_CROSSHAIR_SIZE * LIGHTGUN_CROSSHAIR_SIZE * 4];
u8 *pixels = (u8 *)texture->UploadMemory(LIGHTGUN_CROSSHAIR_SIZE, LIGHTGUN_CROSSHAIR_SIZE, sizeof(temp_tex_buffer), 4, false);
if (pixels == nullptr)
pixels = temp_tex_buffer;
u8 *dst = pixels;
u8* colour = &lightgun_palette[lightgun_params[i].colour * 3];
u8 *src = lightgun_img_crosshair;
for (int y = 0; y < LIGHTGUN_CROSSHAIR_SIZE; y++)
{
for (int x = 0; x < LIGHTGUN_CROSSHAIR_SIZE; x++)
{
if (*src++)
{
*dst++ = colour[0];
*dst++ = colour[1];
*dst++ = colour[2];
*dst++ = 0xFF;
}
else
{
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
}
}
}
commandBuffers[context->GetCurrentImageIndex()].push_back(commandPool->Allocate());
texture->SetCommandBuffer(commandBuffers[context->GetCurrentImageIndex()].back());
texture->UploadToGPU(LIGHTGUN_CROSSHAIR_SIZE, LIGHTGUN_CROSSHAIR_SIZE, pixels, false);
texture->SetCommandBuffer(nullptr);
lightgun_params[i].dirty = false;
}
return &commandBuffers[context->GetCurrentImageIndex()];
}
void VulkanOSD::DrawOSD(vk::CommandBuffer commandBuffer, vk::Extent2D viewport, CommandPool *commandPool, QuadPipeline *with)
{
(with != nullptr ? with : pipeline)->BindPipeline(commandBuffer, false);
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 < VMU_COUNT; i++)
{
if (!vmuTextures[i])
continue;
const auto& vmu_screen_param = vmu_screen_params[i];
float x = 0;
float y = 0;
float w = VMU_SCREEN_WIDTH * vmu_screen_param.vmu_screen_size_mult;
float h = VMU_SCREEN_HEIGHT * vmu_screen_param.vmu_screen_size_mult;
switch (vmu_screen_param.vmu_screen_position)
{
case UPPER_LEFT:
x = 0;
y = 0;
break;
case UPPER_RIGHT:
x = viewport.width - w;
y = 0;
break;
case LOWER_LEFT:
x = 0;
y = viewport.height - h;
break;
case LOWER_RIGHT:
x = viewport.width - w;
y = viewport.height - h;
break;
}
vk::Viewport viewport(x, y, w, h);
commandBuffer.setViewport(0, 1, &viewport);
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(w, h)));
float alpha = vmu_screen_param.vmu_screen_opacity / 255.f;
float blendConstants[4] = { alpha, alpha, alpha, alpha };
commandBuffer.setBlendConstants(blendConstants);
drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true, commandPool);
}
pipeline->BindPipeline(commandBuffer, true);
for (int i = 0; i < XHAIR_COUNT; i++)
{
if (!xhairTextures[i])
continue;
float w = LIGHTGUN_CROSSHAIR_SIZE;
float h = LIGHTGUN_CROSSHAIR_SIZE;
// (the game's 640x480 is in the middle of a widescreen picture)
float sidebar = (viewport.width - viewport.height * 4.f / 3.f) / 2;
float x = sidebar + lightgun_params[i].x * viewport.height / 480 - w / 2;
float y = lightgun_params[i].y * viewport.height / 480 - h / 2;
vk::Viewport viewport(x, y, w, h);
commandBuffer.setViewport(0, 1, &viewport);
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(w, h)));
float blendConstants[4] = { 1.f, 1.f, 1.f, 1.f };
commandBuffer.setBlendConstants(blendConstants);
xhairDrawers[i]->Draw(commandBuffer, xhairTextures[i]->GetImageView(), vtx, true, commandPool);
}
}