/* Copyright 2021 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 . */ #include "dx11_overlay.h" #include "rend/osd.h" #ifdef LIBRETRO #include "vmu_xhair.h" #endif void DX11Overlay::draw(u32 width, u32 height, bool vmu, bool crosshair) { RECT rect { 0, 0, (LONG)width, (LONG)height }; deviceContext->RSSetScissorRects(1, &rect); if (vmu) { #ifndef LIBRETRO float vmu_padding = 8.f * settings.display.uiScale; float vmu_height = 70.f * settings.display.uiScale; float vmu_width = 48.f / 32.f * vmu_height; const float blend_factor[4] = { 0.75f, 0.75f, 0.75f, 0.75f }; deviceContext->OMSetBlendState(blendStates.getState(true, 8, 8), blend_factor, 0xffffffff); #else float vmu_padding_x = 8.f * width / 640.f; float vmu_padding_y = 8.f * height / 480.f; float vmu_width = 48.f * width / 640.f; float vmu_height = 32.f * height / 480.f; if (config::Widescreen) { vmu_padding_x = vmu_padding_x / 4.f * 3.f; vmu_width = vmu_width / 4.f * 3.f; } deviceContext->OMSetBlendState(blendStates.getState(true, 4, 5), nullptr, 0xffffffff); #endif for (size_t i = 0; i < vmuTextures.size(); i++) { if (!vmu_lcd_status[i]) { vmuTextureViews[i].reset(); vmuTextures[i].reset(); continue; } if (vmuTextures[i] == nullptr || this->vmuLastChanged[i] != ::vmuLastChanged[i]) { vmuTextureViews[i].reset(); vmuTextures[i].reset(); D3D11_TEXTURE2D_DESC desc{}; desc.Width = 48; desc.Height = 32; desc.ArraySize = 1; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.MipLevels = 1; if (SUCCEEDED(device->CreateTexture2D(&desc, nullptr, &vmuTextures[i].get()))) { D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; viewDesc.Format = desc.Format; viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipLevels = desc.MipLevels; device->CreateShaderResourceView(vmuTextures[i], &viewDesc, &vmuTextureViews[i].get()); u32 data[48 * 32]; for (int y = 0; y < 32; y++) memcpy(&data[y * 48], &vmu_lcd_data[i][(31 - y) * 48], sizeof(u32) * 48); deviceContext->UpdateSubresource(vmuTextures[i], 0, nullptr, data, 48 * 4, 48 * 4 * 32); this->vmuLastChanged[i] = ::vmuLastChanged[i]; } } float x, y; float w = vmu_width; float h = vmu_height; #ifdef LIBRETRO if (i & 1) continue; w *= (float)vmu_screen_params[i / 2].vmu_screen_size_mult / config::ScreenStretching * 100.f; h *= vmu_screen_params[i / 2].vmu_screen_size_mult; switch (vmu_screen_params[i / 2].vmu_screen_position) { case UPPER_LEFT: default: x = vmu_padding_x; y = vmu_padding_y; break; case UPPER_RIGHT: x = width - vmu_padding_x - w; y = vmu_padding_y; break; case LOWER_LEFT: x = vmu_padding_x; y = height - vmu_padding_y - h; break; case LOWER_RIGHT: x = width - vmu_padding_x - w; y = height - vmu_padding_y - h; break; } #else if (i & 2) x = width - vmu_padding - vmu_width; else x = vmu_padding; if (i & 4) { y = 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; } #endif D3D11_VIEWPORT vp{}; vp.TopLeftX = x; vp.TopLeftY = y; vp.Width = w; vp.Height = h; vp.MinDepth = 0.f; vp.MaxDepth = 1.f; deviceContext->RSSetViewports(1, &vp); quad.draw(vmuTextureViews[i], samplers->getSampler(false)); } } if (crosshair) { for (u32 i = 0; i < config::CrosshairColor.size(); i++) { if (!crosshairNeeded(i)) continue; if (!xhairTexture) { const u32* texData = getCrosshairTextureData(); D3D11_TEXTURE2D_DESC desc{}; desc.Width = 16; desc.Height = 16; desc.ArraySize = 1; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.MipLevels = 1; if (SUCCEEDED(device->CreateTexture2D(&desc, nullptr, &xhairTexture.get()))) { D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; viewDesc.Format = desc.Format; viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipLevels = desc.MipLevels; device->CreateShaderResourceView(xhairTexture, &viewDesc, &xhairTextureView.get()); deviceContext->UpdateSubresource(xhairTexture, 0, nullptr, texData, 16 * 4, 16 * 4 * 16); } } auto [x, y] = getCrosshairPosition(i); #ifdef LIBRETRO float halfWidth = lightgun_crosshair_size / 2.f / config::ScreenStretching * 100.f * config::RenderResolution / 480.f; float halfHeight = lightgun_crosshair_size / 2.f * config::RenderResolution / 480.f; x /= config::ScreenStretching / 100.f; #else float halfWidth = config::CrosshairSize * settings.display.uiScale / 2.f; float halfHeight = halfWidth; #endif D3D11_VIEWPORT vp{}; vp.TopLeftX = x - halfWidth; vp.TopLeftY = y - halfHeight; vp.Width = halfWidth * 2; vp.Height = halfHeight * 2; vp.MinDepth = 0.f; vp.MaxDepth = 1.f; deviceContext->RSSetViewports(1, &vp); const float colors[4] = { (config::CrosshairColor[i] & 0xff) / 255.f, ((config::CrosshairColor[i] >> 8) & 0xff) / 255.f, ((config::CrosshairColor[i] >> 16) & 0xff) / 255.f, ((config::CrosshairColor[i] >> 24) & 0xff) / 255.f }; deviceContext->OMSetBlendState(blendStates.getState(true, 4, 5), nullptr, 0xffffffff); quad.draw(xhairTextureView, samplers->getSampler(false), colors); } } }