2024-07-02 10:49:13 +00:00
|
|
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
2022-12-04 11:03:45 +00:00
|
|
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
|
|
|
2019-10-26 02:57:35 +00:00
|
|
|
#include "gpu_sw.h"
|
2023-08-13 03:42:02 +00:00
|
|
|
#include "system.h"
|
|
|
|
|
|
|
|
#include "util/gpu_device.h"
|
|
|
|
|
2020-10-21 15:25:33 +00:00
|
|
|
#include "common/align.h"
|
2020-01-10 03:31:12 +00:00
|
|
|
#include "common/assert.h"
|
2024-07-02 10:49:13 +00:00
|
|
|
#include "common/gsvector.h"
|
2020-05-21 02:04:53 +00:00
|
|
|
#include "common/log.h"
|
2023-08-13 03:42:02 +00:00
|
|
|
|
2019-10-26 02:57:35 +00:00
|
|
|
#include <algorithm>
|
2023-08-13 03:42:02 +00:00
|
|
|
|
2020-05-21 02:04:53 +00:00
|
|
|
Log_SetChannel(GPU_SW);
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
template<typename T>
|
|
|
|
ALWAYS_INLINE static constexpr std::tuple<T, T> MinMax(T v1, T v2)
|
|
|
|
{
|
|
|
|
if (v1 > v2)
|
|
|
|
return std::tie(v2, v1);
|
|
|
|
else
|
|
|
|
return std::tie(v1, v2);
|
|
|
|
}
|
|
|
|
|
2023-12-23 06:53:15 +00:00
|
|
|
GPU_SW::GPU_SW() = default;
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2019-11-03 14:39:48 +00:00
|
|
|
GPU_SW::~GPU_SW()
|
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
g_gpu_device->RecycleTexture(std::move(m_upload_texture));
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.Shutdown();
|
2019-11-03 14:39:48 +00:00
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2023-08-13 03:42:02 +00:00
|
|
|
const Threading::Thread* GPU_SW::GetSWThread() const
|
2020-01-24 04:51:53 +00:00
|
|
|
{
|
2023-08-13 03:42:02 +00:00
|
|
|
return m_backend.GetThread();
|
2020-01-24 04:51:53 +00:00
|
|
|
}
|
|
|
|
|
2023-08-13 03:42:02 +00:00
|
|
|
bool GPU_SW::IsHardwareRenderer() const
|
2022-08-05 07:17:29 +00:00
|
|
|
{
|
2023-08-13 03:42:02 +00:00
|
|
|
return false;
|
2022-08-05 07:17:29 +00:00
|
|
|
}
|
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
bool GPU_SW::Initialize()
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2022-07-11 13:03:29 +00:00
|
|
|
if (!GPU::Initialize() || !m_backend.Initialize(false))
|
2019-11-03 14:39:48 +00:00
|
|
|
return false;
|
|
|
|
|
2023-10-02 03:33:33 +00:00
|
|
|
static constexpr const std::array formats_for_16bit = {GPUTexture::Format::RGB565, GPUTexture::Format::RGBA5551,
|
|
|
|
GPUTexture::Format::RGBA8, GPUTexture::Format::BGRA8};
|
|
|
|
static constexpr const std::array formats_for_24bit = {GPUTexture::Format::RGBA8, GPUTexture::Format::BGRA8,
|
|
|
|
GPUTexture::Format::RGB565, GPUTexture::Format::RGBA5551};
|
2022-10-03 06:44:34 +00:00
|
|
|
for (const GPUTexture::Format format : formats_for_16bit)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2023-08-13 03:42:02 +00:00
|
|
|
if (g_gpu_device->SupportsTextureFormat(format))
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
m_16bit_display_format = format;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
for (const GPUTexture::Format format : formats_for_24bit)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2023-08-13 03:42:02 +00:00
|
|
|
if (g_gpu_device->SupportsTextureFormat(format))
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
m_24bit_display_format = format;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
bool GPU_SW::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display)
|
2021-01-23 16:52:52 +00:00
|
|
|
{
|
2024-06-27 06:07:49 +00:00
|
|
|
// need to ensure the worker thread is done
|
|
|
|
m_backend.Sync(true);
|
|
|
|
|
2021-01-23 16:52:52 +00:00
|
|
|
// ignore the host texture for software mode, since we want to save vram here
|
|
|
|
return GPU::DoState(sw, nullptr, update_display);
|
|
|
|
}
|
|
|
|
|
2021-01-23 09:00:54 +00:00
|
|
|
void GPU_SW::Reset(bool clear_vram)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2021-01-23 09:00:54 +00:00
|
|
|
GPU::Reset(clear_vram);
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2023-12-23 06:53:15 +00:00
|
|
|
m_backend.Reset();
|
2020-11-21 03:32:58 +00:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:37:17 +00:00
|
|
|
void GPU_SW::UpdateSettings(const Settings& old_settings)
|
2020-11-21 03:32:58 +00:00
|
|
|
{
|
2023-08-31 13:37:17 +00:00
|
|
|
GPU::UpdateSettings(old_settings);
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.UpdateSettings();
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format)
|
2022-09-10 15:54:01 +00:00
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
if (!m_upload_texture || m_upload_texture->GetWidth() != width || m_upload_texture->GetHeight() != height ||
|
|
|
|
m_upload_texture->GetFormat() != format)
|
2022-09-10 15:54:01 +00:00
|
|
|
{
|
2023-08-27 08:13:50 +00:00
|
|
|
ClearDisplayTexture();
|
2024-03-15 16:02:03 +00:00
|
|
|
g_gpu_device->RecycleTexture(std::move(m_upload_texture));
|
|
|
|
m_upload_texture =
|
2023-12-04 06:04:45 +00:00
|
|
|
g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::DynamicTexture, format, nullptr, 0);
|
2024-05-23 10:20:16 +00:00
|
|
|
if (!m_upload_texture) [[unlikely]]
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to create {}x{} {} texture", width, height, static_cast<u32>(format));
|
2022-09-10 15:54:01 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
return m_upload_texture.get();
|
2022-09-10 15:54:01 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
template<GPUTexture::Format out_format, typename out_type>
|
2020-10-21 15:25:33 +00:00
|
|
|
static void CopyOutRow16(const u16* src_ptr, out_type* dst_ptr, u32 width);
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
template<GPUTexture::Format out_format, typename out_type>
|
2020-10-21 15:25:33 +00:00
|
|
|
static out_type VRAM16ToOutput(u16 value);
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE u16 VRAM16ToOutput<GPUTexture::Format::RGBA5551, u16>(u16 value)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
return (value & 0x3E0) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE u16 VRAM16ToOutput<GPUTexture::Format::RGB565, u16>(u16 value)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
return ((value & 0x3E0) << 1) | ((value & 0x20) << 1) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 11);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE u32 VRAM16ToOutput<GPUTexture::Format::RGBA8, u32>(u16 value)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2022-12-20 11:19:05 +00:00
|
|
|
const u32 value32 = ZeroExtend32(value);
|
|
|
|
const u32 r = (value32 & 31u) << 3;
|
|
|
|
const u32 g = ((value32 >> 5) & 31u) << 3;
|
|
|
|
const u32 b = ((value32 >> 10) & 31u) << 3;
|
|
|
|
const u32 a = ((value >> 15) != 0) ? 255 : 0;
|
|
|
|
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE u32 VRAM16ToOutput<GPUTexture::Format::BGRA8, u32>(u16 value)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2021-03-18 02:55:02 +00:00
|
|
|
const u32 value32 = ZeroExtend32(value);
|
2022-12-20 11:19:05 +00:00
|
|
|
const u32 r = (value32 & 31u) << 3;
|
|
|
|
const u32 g = ((value32 >> 5) & 31u) << 3;
|
|
|
|
const u32 b = ((value32 >> 10) & 31u) << 3;
|
2020-10-21 15:25:33 +00:00
|
|
|
return ZeroExtend32(b) | (ZeroExtend32(g) << 8) | (ZeroExtend32(r) << 16) | (0xFF000000u);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE void CopyOutRow16<GPUTexture::Format::RGBA5551, u16>(const u16* src_ptr, u16* dst_ptr, u32 width)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
u32 col = 0;
|
|
|
|
|
|
|
|
const u32 aligned_width = Common::AlignDownPow2(width, 8);
|
|
|
|
for (; col < aligned_width; col += 8)
|
|
|
|
{
|
2024-07-02 10:49:13 +00:00
|
|
|
constexpr GSVector4i single_mask = GSVector4i::cxpr16(0x1F);
|
|
|
|
GSVector4i value = GSVector4i::load<false>(src_ptr);
|
2020-10-21 15:25:33 +00:00
|
|
|
src_ptr += 8;
|
2024-07-02 10:49:13 +00:00
|
|
|
GSVector4i a = value & GSVector4i::cxpr16(0x3E0);
|
|
|
|
GSVector4i b = value.srl16<10>() & single_mask;
|
|
|
|
GSVector4i c = (value & single_mask).sll16<10>();
|
|
|
|
value = (a | b) | c;
|
|
|
|
GSVector4i::store<false>(dst_ptr, value);
|
2020-10-21 15:25:33 +00:00
|
|
|
dst_ptr += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; col < width; col++)
|
2022-10-03 06:44:34 +00:00
|
|
|
*(dst_ptr++) = VRAM16ToOutput<GPUTexture::Format::RGBA5551, u16>(*(src_ptr++));
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE void CopyOutRow16<GPUTexture::Format::RGB565, u16>(const u16* src_ptr, u16* dst_ptr, u32 width)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-10-21 15:25:33 +00:00
|
|
|
u32 col = 0;
|
|
|
|
|
|
|
|
const u32 aligned_width = Common::AlignDownPow2(width, 8);
|
|
|
|
for (; col < aligned_width; col += 8)
|
|
|
|
{
|
2024-07-02 10:49:13 +00:00
|
|
|
constexpr GSVector4i single_mask = GSVector4i::cxpr16(0x1F);
|
|
|
|
GSVector4i value = GSVector4i::load<false>(src_ptr);
|
2020-10-21 15:25:33 +00:00
|
|
|
src_ptr += 8;
|
2024-07-02 10:49:13 +00:00
|
|
|
GSVector4i a = (value & GSVector4i::cxpr16(0x3E0)).sll16<1>(); // (value & 0x3E0) << 1
|
|
|
|
GSVector4i b = (value & GSVector4i::cxpr16(0x20)).sll16<1>(); // (value & 0x20) << 1
|
|
|
|
GSVector4i c = (value.srl16<10>() & single_mask); // ((value >> 10) & 0x1F)
|
|
|
|
GSVector4i d = (value & single_mask).sll16<11>(); // ((value & 0x1F) << 11)
|
|
|
|
value = (((a | b) | c) | d);
|
|
|
|
GSVector4i::store<false>(dst_ptr, value);
|
2020-10-21 15:25:33 +00:00
|
|
|
dst_ptr += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; col < width; col++)
|
2022-10-03 06:44:34 +00:00
|
|
|
*(dst_ptr++) = VRAM16ToOutput<GPUTexture::Format::RGB565, u16>(*(src_ptr++));
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE void CopyOutRow16<GPUTexture::Format::RGBA8, u32>(const u16* src_ptr, u32* dst_ptr, u32 width)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
for (u32 col = 0; col < width; col++)
|
2022-10-03 06:44:34 +00:00
|
|
|
*(dst_ptr++) = VRAM16ToOutput<GPUTexture::Format::RGBA8, u32>(*(src_ptr++));
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2022-10-03 06:44:34 +00:00
|
|
|
ALWAYS_INLINE void CopyOutRow16<GPUTexture::Format::BGRA8, u32>(const u16* src_ptr, u32* dst_ptr, u32 width)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
for (u32 col = 0; col < width; col++)
|
2022-10-03 06:44:34 +00:00
|
|
|
*(dst_ptr++) = VRAM16ToOutput<GPUTexture::Format::BGRA8, u32>(*(src_ptr++));
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
template<GPUTexture::Format display_format>
|
2024-03-15 16:02:03 +00:00
|
|
|
ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 line_skip)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2023-01-28 11:38:03 +00:00
|
|
|
using OutputPixelType =
|
|
|
|
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
|
|
|
u16>;
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
2024-03-15 16:02:03 +00:00
|
|
|
if (!texture) [[unlikely]]
|
|
|
|
return false;
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
u32 dst_stride = width * sizeof(OutputPixelType);
|
|
|
|
u8* dst_ptr = m_upload_buffer.data();
|
|
|
|
const bool mapped = texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height);
|
2020-04-11 06:09:03 +00:00
|
|
|
|
|
|
|
// Fast path when not wrapping around.
|
|
|
|
if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2023-12-23 06:53:15 +00:00
|
|
|
const u16* src_ptr = &g_vram[src_y * VRAM_WIDTH + src_x];
|
2024-03-15 16:02:03 +00:00
|
|
|
const u32 src_step = VRAM_WIDTH << line_skip;
|
|
|
|
for (u32 row = 0; row < height; row++)
|
2020-04-11 06:09:03 +00:00
|
|
|
{
|
2020-10-21 15:25:33 +00:00
|
|
|
CopyOutRow16<display_format>(src_ptr, reinterpret_cast<OutputPixelType*>(dst_ptr), width);
|
|
|
|
src_ptr += src_step;
|
2020-04-11 06:09:03 +00:00
|
|
|
dst_ptr += dst_stride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const u32 end_x = src_x + width;
|
2024-03-15 16:02:03 +00:00
|
|
|
const u32 y_step = (1 << line_skip);
|
|
|
|
for (u32 row = 0; row < height; row++)
|
2020-04-11 06:09:03 +00:00
|
|
|
{
|
2023-12-23 06:53:15 +00:00
|
|
|
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
2020-10-21 15:25:33 +00:00
|
|
|
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
2020-11-27 07:58:06 +00:00
|
|
|
|
2020-04-11 06:09:03 +00:00
|
|
|
for (u32 col = src_x; col < end_x; col++)
|
2020-10-21 15:25:33 +00:00
|
|
|
*(dst_row_ptr++) = VRAM16ToOutput<display_format, OutputPixelType>(src_row_ptr[col % VRAM_WIDTH]);
|
2020-11-27 07:58:06 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
src_y += y_step;
|
2020-11-27 07:58:06 +00:00
|
|
|
dst_ptr += dst_stride;
|
2020-04-11 06:09:03 +00:00
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2023-09-20 09:35:43 +00:00
|
|
|
if (mapped)
|
2023-08-13 03:42:02 +00:00
|
|
|
texture->Unmap();
|
2020-10-21 15:25:33 +00:00
|
|
|
else
|
2024-03-15 16:02:03 +00:00
|
|
|
texture->Update(0, 0, width, height, m_upload_buffer.data(), dst_stride);
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
return true;
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
template<GPUTexture::Format display_format>
|
2024-03-15 16:02:03 +00:00
|
|
|
ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2023-01-28 11:38:03 +00:00
|
|
|
using OutputPixelType =
|
|
|
|
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
|
|
|
u16>;
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
2024-03-15 16:02:03 +00:00
|
|
|
if (!texture) [[unlikely]]
|
|
|
|
return false;
|
2022-09-10 15:54:01 +00:00
|
|
|
|
2023-09-20 09:35:43 +00:00
|
|
|
u32 dst_stride = Common::AlignUpPow2<u32>(width * sizeof(OutputPixelType), 4);
|
2024-03-15 16:02:03 +00:00
|
|
|
u8* dst_ptr = m_upload_buffer.data();
|
|
|
|
const bool mapped = texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height);
|
2020-04-11 06:09:03 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
if ((src_x + width) <= VRAM_WIDTH && (src_y + (height << line_skip)) <= VRAM_HEIGHT)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2023-12-23 06:53:15 +00:00
|
|
|
const u8* src_ptr = reinterpret_cast<const u8*>(&g_vram[src_y * VRAM_WIDTH + src_x]) + (skip_x * 3);
|
2024-03-15 16:02:03 +00:00
|
|
|
const u32 src_stride = (VRAM_WIDTH << line_skip) * sizeof(u16);
|
|
|
|
for (u32 row = 0; row < height; row++)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2022-10-03 06:44:34 +00:00
|
|
|
if constexpr (display_format == GPUTexture::Format::RGBA8)
|
2020-04-11 06:09:03 +00:00
|
|
|
{
|
2020-10-21 15:25:33 +00:00
|
|
|
const u8* src_row_ptr = src_ptr;
|
|
|
|
u8* dst_row_ptr = reinterpret_cast<u8*>(dst_ptr);
|
|
|
|
for (u32 col = 0; col < width; col++)
|
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = *(src_row_ptr++);
|
|
|
|
*(dst_row_ptr++) = *(src_row_ptr++);
|
|
|
|
*(dst_row_ptr++) = *(src_row_ptr++);
|
|
|
|
*(dst_row_ptr++) = 0xFF;
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::BGRA8)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
const u8* src_row_ptr = src_ptr;
|
|
|
|
u8* dst_row_ptr = reinterpret_cast<u8*>(dst_ptr);
|
|
|
|
for (u32 col = 0; col < width; col++)
|
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = src_row_ptr[2];
|
|
|
|
*(dst_row_ptr++) = src_row_ptr[1];
|
|
|
|
*(dst_row_ptr++) = src_row_ptr[0];
|
|
|
|
*(dst_row_ptr++) = 0xFF;
|
|
|
|
src_row_ptr += 3;
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::RGB565)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
const u8* src_row_ptr = src_ptr;
|
|
|
|
u16* dst_row_ptr = reinterpret_cast<u16*>(dst_ptr);
|
|
|
|
for (u32 col = 0; col < width; col++)
|
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = ((static_cast<u16>(src_row_ptr[0]) >> 3) << 11) |
|
|
|
|
((static_cast<u16>(src_row_ptr[1]) >> 2) << 5) | (static_cast<u16>(src_row_ptr[2]) >> 3);
|
|
|
|
src_row_ptr += 3;
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::RGBA5551)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
const u8* src_row_ptr = src_ptr;
|
|
|
|
u16* dst_row_ptr = reinterpret_cast<u16*>(dst_ptr);
|
|
|
|
for (u32 col = 0; col < width; col++)
|
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = ((static_cast<u16>(src_row_ptr[0]) >> 3) << 10) |
|
|
|
|
((static_cast<u16>(src_row_ptr[1]) >> 3) << 5) | (static_cast<u16>(src_row_ptr[2]) >> 3);
|
|
|
|
src_row_ptr += 3;
|
|
|
|
}
|
2020-04-11 06:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
src_ptr += src_stride;
|
|
|
|
dst_ptr += dst_stride;
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
2020-04-11 06:09:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
const u32 y_step = (1 << line_skip);
|
|
|
|
|
|
|
|
for (u32 row = 0; row < height; row++)
|
2020-04-11 06:09:03 +00:00
|
|
|
{
|
2023-12-23 06:53:15 +00:00
|
|
|
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
2020-10-21 15:25:33 +00:00
|
|
|
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
2020-04-11 06:09:03 +00:00
|
|
|
|
|
|
|
for (u32 col = 0; col < width; col++)
|
|
|
|
{
|
2020-10-21 15:25:33 +00:00
|
|
|
const u32 offset = (src_x + (((skip_x + col) * 3) / 2));
|
2020-04-11 06:09:03 +00:00
|
|
|
const u16 s0 = src_row_ptr[offset % VRAM_WIDTH];
|
|
|
|
const u16 s1 = src_row_ptr[(offset + 1) % VRAM_WIDTH];
|
|
|
|
const u8 shift = static_cast<u8>(col & 1u) * 8;
|
2020-10-21 15:25:33 +00:00
|
|
|
const u32 rgb = (((ZeroExtend32(s1) << 16) | ZeroExtend32(s0)) >> shift);
|
|
|
|
|
2022-10-03 06:44:34 +00:00
|
|
|
if constexpr (display_format == GPUTexture::Format::RGBA8)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = rgb | 0xFF000000u;
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::BGRA8)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = (rgb & 0x00FF00) | ((rgb & 0xFF) << 16) | ((rgb >> 16) & 0xFF) | 0xFF000000u;
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::RGB565)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 10) << 5) & 0x7E0) | (((rgb >> 19) << 11) & 0x3E0000);
|
|
|
|
}
|
2022-10-03 06:44:34 +00:00
|
|
|
else if constexpr (display_format == GPUTexture::Format::RGBA5551)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
|
|
|
*(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 11) << 5) & 0x3E0) | (((rgb >> 19) << 10) & 0x1F0000);
|
|
|
|
}
|
2020-04-11 06:09:03 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
src_y += y_step;
|
2020-04-11 06:09:03 +00:00
|
|
|
dst_ptr += dst_stride;
|
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2023-09-20 09:35:43 +00:00
|
|
|
if (mapped)
|
2023-08-13 03:42:02 +00:00
|
|
|
texture->Unmap();
|
2020-10-21 15:25:33 +00:00
|
|
|
else
|
2024-03-15 16:02:03 +00:00
|
|
|
texture->Update(0, 0, width, height, m_upload_buffer.data(), dst_stride);
|
2022-09-10 15:54:01 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
return true;
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
bool GPU_SW::CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip, bool is_24bit)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
if (!is_24bit)
|
2020-10-21 15:25:33 +00:00
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
DebugAssert(skip_x == 0);
|
|
|
|
|
|
|
|
switch (m_16bit_display_format)
|
|
|
|
{
|
|
|
|
case GPUTexture::Format::RGBA5551:
|
|
|
|
return CopyOut15Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, width, height, line_skip);
|
|
|
|
|
|
|
|
case GPUTexture::Format::RGB565:
|
|
|
|
return CopyOut15Bit<GPUTexture::Format::RGB565>(src_x, src_y, width, height, line_skip);
|
|
|
|
|
|
|
|
case GPUTexture::Format::RGBA8:
|
|
|
|
return CopyOut15Bit<GPUTexture::Format::RGBA8>(src_x, src_y, width, height, line_skip);
|
|
|
|
|
|
|
|
case GPUTexture::Format::BGRA8:
|
|
|
|
return CopyOut15Bit<GPUTexture::Format::BGRA8>(src_x, src_y, width, height, line_skip);
|
|
|
|
|
|
|
|
default:
|
|
|
|
UnreachableCode();
|
|
|
|
}
|
2020-10-21 15:25:33 +00:00
|
|
|
}
|
2024-03-15 16:02:03 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (m_24bit_display_format)
|
|
|
|
{
|
|
|
|
case GPUTexture::Format::RGBA5551:
|
|
|
|
return CopyOut24Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, skip_x, width, height, line_skip);
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
case GPUTexture::Format::RGB565:
|
|
|
|
return CopyOut24Bit<GPUTexture::Format::RGB565>(src_x, src_y, skip_x, width, height, line_skip);
|
|
|
|
|
|
|
|
case GPUTexture::Format::RGBA8:
|
|
|
|
return CopyOut24Bit<GPUTexture::Format::RGBA8>(src_x, src_y, skip_x, width, height, line_skip);
|
|
|
|
|
|
|
|
case GPUTexture::Format::BGRA8:
|
|
|
|
return CopyOut24Bit<GPUTexture::Format::BGRA8>(src_x, src_y, skip_x, width, height, line_skip);
|
|
|
|
|
|
|
|
default:
|
|
|
|
UnreachableCode();
|
|
|
|
}
|
|
|
|
}
|
2020-08-02 17:26:11 +00:00
|
|
|
}
|
|
|
|
|
2019-10-26 02:57:35 +00:00
|
|
|
void GPU_SW::UpdateDisplay()
|
|
|
|
{
|
|
|
|
// fill display texture
|
2021-04-17 12:16:59 +00:00
|
|
|
m_backend.Sync(true);
|
2020-11-21 03:32:58 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
if (!g_settings.debugging.show_vram)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-04-27 11:49:34 +00:00
|
|
|
if (IsDisplayDisabled())
|
2019-10-28 07:43:34 +00:00
|
|
|
{
|
2023-08-27 08:13:50 +00:00
|
|
|
ClearDisplayTexture();
|
2019-10-28 07:43:34 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-04-09 10:50:12 +00:00
|
|
|
|
2024-03-15 16:02:03 +00:00
|
|
|
const bool is_24bit = m_GPUSTAT.display_area_color_depth_24;
|
|
|
|
const bool interlaced = IsInterlacedDisplayEnabled();
|
|
|
|
const u32 field = GetInterlacedDisplayField();
|
|
|
|
const u32 vram_offset_x = is_24bit ? m_crtc_state.regs.X : m_crtc_state.display_vram_left;
|
|
|
|
const u32 vram_offset_y =
|
|
|
|
m_crtc_state.display_vram_top + ((interlaced && m_GPUSTAT.vertical_resolution) ? field : 0);
|
|
|
|
const u32 skip_x = is_24bit ? (m_crtc_state.display_vram_left - m_crtc_state.regs.X) : 0;
|
|
|
|
const u32 read_width = m_crtc_state.display_vram_width;
|
|
|
|
const u32 read_height = interlaced ? (m_crtc_state.display_vram_height / 2) : m_crtc_state.display_vram_height;
|
2020-10-21 15:25:33 +00:00
|
|
|
|
2020-04-09 10:50:12 +00:00
|
|
|
if (IsInterlacedDisplayEnabled())
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
const u32 line_skip = m_GPUSTAT.vertical_resolution;
|
|
|
|
if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, line_skip, is_24bit))
|
2020-04-03 14:11:43 +00:00
|
|
|
{
|
2024-03-24 09:49:16 +00:00
|
|
|
SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, read_width, read_height);
|
2024-03-15 16:02:03 +00:00
|
|
|
if (is_24bit && g_settings.gpu_24bit_chroma_smoothing)
|
|
|
|
{
|
2024-06-28 05:37:26 +00:00
|
|
|
if (ApplyChromaSmoothing())
|
|
|
|
Deinterlace(field, 0);
|
2024-03-15 16:02:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-06-28 05:37:26 +00:00
|
|
|
Deinterlace(field, 0);
|
2024-03-15 16:02:03 +00:00
|
|
|
}
|
2020-04-03 14:11:43 +00:00
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, 0, is_24bit))
|
2020-04-03 14:11:43 +00:00
|
|
|
{
|
2024-03-24 09:49:16 +00:00
|
|
|
SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, read_width, read_height);
|
2024-03-15 16:02:03 +00:00
|
|
|
if (is_24bit && g_settings.gpu_24bit_chroma_smoothing)
|
2024-06-28 05:37:26 +00:00
|
|
|
ApplyChromaSmoothing();
|
2020-04-03 14:11:43 +00:00
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-15 16:02:03 +00:00
|
|
|
if (CopyOut(0, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false))
|
2024-03-24 09:49:16 +00:00
|
|
|
SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-13 17:36:25 +00:00
|
|
|
void GPU_SW::FillBackendCommandParameters(GPUBackendCommand* cmd) const
|
2020-11-21 03:32:58 +00:00
|
|
|
{
|
|
|
|
cmd->params.bits = 0;
|
|
|
|
cmd->params.check_mask_before_draw = m_GPUSTAT.check_mask_before_draw;
|
|
|
|
cmd->params.set_mask_while_drawing = m_GPUSTAT.set_mask_while_drawing;
|
|
|
|
cmd->params.active_line_lsb = m_crtc_state.active_line_lsb;
|
|
|
|
cmd->params.interlaced_rendering = IsInterlacedRenderingEnabled();
|
|
|
|
}
|
|
|
|
|
2021-05-13 17:36:25 +00:00
|
|
|
void GPU_SW::FillDrawCommand(GPUBackendDrawCommand* cmd, GPURenderCommand rc) const
|
2020-11-21 03:32:58 +00:00
|
|
|
{
|
|
|
|
FillBackendCommandParameters(cmd);
|
|
|
|
cmd->rc.bits = rc.bits;
|
|
|
|
cmd->draw_mode.bits = m_draw_mode.mode_reg.bits;
|
2023-12-14 09:05:18 +00:00
|
|
|
cmd->palette.bits = m_draw_mode.palette_reg.bits;
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->window = m_draw_mode.texture_window;
|
|
|
|
}
|
|
|
|
|
2020-04-18 15:16:58 +00:00
|
|
|
void GPU_SW::DispatchRenderCommand()
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
if (m_drawing_area_changed)
|
|
|
|
{
|
|
|
|
GPUBackendSetDrawingAreaCommand* cmd = m_backend.NewSetDrawingAreaCommand();
|
|
|
|
cmd->new_area = m_drawing_area;
|
|
|
|
m_backend.PushCommand(cmd);
|
|
|
|
m_drawing_area_changed = false;
|
|
|
|
}
|
|
|
|
|
2020-10-22 09:31:28 +00:00
|
|
|
const GPURenderCommand rc{m_render_command.bits};
|
2019-12-22 09:34:24 +00:00
|
|
|
|
2019-10-26 02:57:35 +00:00
|
|
|
switch (rc.primitive)
|
|
|
|
{
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUPrimitive::Polygon:
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
const u32 num_vertices = rc.quad_polygon ? 4 : 3;
|
|
|
|
GPUBackendDrawPolygonCommand* cmd = m_backend.NewDrawPolygonCommand(num_vertices);
|
|
|
|
FillDrawCommand(cmd, rc);
|
|
|
|
|
2024-06-01 07:04:35 +00:00
|
|
|
std::array<GSVector4i, 4> positions;
|
2019-10-26 02:57:35 +00:00
|
|
|
const u32 first_color = rc.color_for_first_vertex;
|
|
|
|
const bool shaded = rc.shading_enable;
|
|
|
|
const bool textured = rc.texture_enable;
|
|
|
|
for (u32 i = 0; i < num_vertices; i++)
|
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendDrawPolygonCommand::Vertex* vert = &cmd->vertices[i];
|
|
|
|
vert->color = (shaded && i > 0) ? (FifoPop() & UINT32_C(0x00FFFFFF)) : first_color;
|
|
|
|
const u64 maddr_and_pos = m_fifo.Pop();
|
|
|
|
const GPUVertexPosition vp{Truncate32(maddr_and_pos)};
|
|
|
|
vert->x = m_drawing_offset.x + vp.x;
|
|
|
|
vert->y = m_drawing_offset.y + vp.y;
|
|
|
|
vert->texcoord = textured ? Truncate16(FifoPop()) : 0;
|
2024-06-01 07:04:35 +00:00
|
|
|
positions[i] = GSVector4i::loadl(&vert->x);
|
2020-11-21 03:32:58 +00:00
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
if (!IsDrawingAreaIsValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Cull polygons which are too large.
|
2024-06-01 07:04:35 +00:00
|
|
|
const GSVector4i min_pos_12 = positions[1].min_i32(positions[2]);
|
|
|
|
const GSVector4i max_pos_12 = positions[1].max_i32(positions[2]);
|
|
|
|
const GSVector4i draw_rect_012 =
|
|
|
|
min_pos_12.min_i32(positions[0]).upl64(max_pos_12.max_i32(positions[0])).add32(GSVector4i::cxpr(0, 0, 1, 1));
|
|
|
|
const bool first_tri_culled =
|
|
|
|
(draw_rect_012.width() > MAX_PRIMITIVE_WIDTH || draw_rect_012.height() > MAX_PRIMITIVE_HEIGHT ||
|
|
|
|
!m_clamped_drawing_area.rintersects(draw_rect_012));
|
|
|
|
if (first_tri_culled)
|
2020-11-21 03:32:58 +00:00
|
|
|
{
|
2024-06-01 07:04:35 +00:00
|
|
|
DEBUG_LOG("Culling off-screen/too-large polygon: {},{} {},{} {},{}", cmd->vertices[0].x, cmd->vertices[0].y,
|
2024-05-23 10:55:28 +00:00
|
|
|
cmd->vertices[1].x, cmd->vertices[1].y, cmd->vertices[2].x, cmd->vertices[2].y);
|
2024-06-01 07:04:35 +00:00
|
|
|
|
|
|
|
if (!rc.quad_polygon)
|
|
|
|
return;
|
2020-11-21 03:32:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-06-01 07:04:35 +00:00
|
|
|
AddDrawTriangleTicks(positions[0], positions[1], positions[2], rc.shading_enable, rc.texture_enable,
|
2020-11-21 03:32:58 +00:00
|
|
|
rc.transparency_enable);
|
|
|
|
}
|
|
|
|
|
|
|
|
// quads
|
|
|
|
if (rc.quad_polygon)
|
|
|
|
{
|
2024-06-01 07:04:35 +00:00
|
|
|
const GSVector4i draw_rect_123 =
|
|
|
|
min_pos_12.min_i32(positions[3]).upl64(max_pos_12.max_i32(positions[3])).add32(GSVector4i::cxpr(0, 0, 1, 1));
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
// Cull polygons which are too large.
|
2024-06-01 07:04:35 +00:00
|
|
|
const bool second_tri_culled =
|
|
|
|
(draw_rect_123.width() > MAX_PRIMITIVE_WIDTH || draw_rect_123.height() > MAX_PRIMITIVE_HEIGHT ||
|
|
|
|
!m_clamped_drawing_area.rintersects(draw_rect_123));
|
|
|
|
if (second_tri_culled)
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG("Culling too-large polygon (quad second half): {},{} {},{} {},{}", cmd->vertices[2].x,
|
|
|
|
cmd->vertices[2].y, cmd->vertices[1].x, cmd->vertices[1].y, cmd->vertices[0].x, cmd->vertices[0].y);
|
2024-06-01 07:04:35 +00:00
|
|
|
|
|
|
|
if (first_tri_culled)
|
|
|
|
return;
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-06-01 07:04:35 +00:00
|
|
|
AddDrawTriangleTicks(positions[2], positions[1], positions[3], rc.shading_enable, rc.texture_enable,
|
2020-11-21 03:32:58 +00:00
|
|
|
rc.transparency_enable);
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.PushCommand(cmd);
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUPrimitive::Rectangle:
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendDrawRectangleCommand* cmd = m_backend.NewDrawRectangleCommand();
|
|
|
|
FillDrawCommand(cmd, rc);
|
|
|
|
cmd->color = rc.color_for_first_vertex;
|
|
|
|
|
2020-10-22 09:31:28 +00:00
|
|
|
const GPUVertexPosition vp{FifoPop()};
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->x = TruncateGPUVertexPosition(m_drawing_offset.x + vp.x);
|
|
|
|
cmd->y = TruncateGPUVertexPosition(m_drawing_offset.y + vp.y);
|
|
|
|
|
|
|
|
if (rc.texture_enable)
|
|
|
|
{
|
|
|
|
const u32 texcoord_and_palette = FifoPop();
|
|
|
|
cmd->palette.bits = Truncate16(texcoord_and_palette >> 16);
|
|
|
|
cmd->texcoord = Truncate16(texcoord_and_palette);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cmd->palette.bits = 0;
|
|
|
|
cmd->texcoord = 0;
|
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
|
|
|
|
switch (rc.rectangle_size)
|
|
|
|
{
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUDrawRectangleSize::R1x1:
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->width = 1;
|
|
|
|
cmd->height = 1;
|
2019-10-26 02:57:35 +00:00
|
|
|
break;
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUDrawRectangleSize::R8x8:
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->width = 8;
|
|
|
|
cmd->height = 8;
|
2019-10-26 02:57:35 +00:00
|
|
|
break;
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUDrawRectangleSize::R16x16:
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->width = 16;
|
|
|
|
cmd->height = 16;
|
2019-10-26 02:57:35 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-04-18 15:16:58 +00:00
|
|
|
{
|
2020-08-01 14:25:07 +00:00
|
|
|
const u32 width_and_height = FifoPop();
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->width = static_cast<u16>(width_and_height & VRAM_WIDTH_MASK);
|
|
|
|
cmd->height = static_cast<u16>((width_and_height >> 16) & VRAM_HEIGHT_MASK);
|
2020-04-18 15:16:58 +00:00
|
|
|
}
|
|
|
|
break;
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
|
2020-06-13 15:01:01 +00:00
|
|
|
if (!IsDrawingAreaIsValid())
|
|
|
|
return;
|
|
|
|
|
2024-05-01 10:58:47 +00:00
|
|
|
AddDrawRectangleTicks(cmd->x, cmd->y, cmd->width, cmd->height, rc.texture_enable, rc.transparency_enable);
|
2019-12-22 09:34:24 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.PushCommand(cmd);
|
2019-10-26 02:57:35 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-10-22 09:31:28 +00:00
|
|
|
case GPUPrimitive::Line:
|
2019-10-26 02:57:35 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
if (!rc.polyline)
|
2019-12-22 08:53:20 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendDrawLineCommand* cmd = m_backend.NewDrawLineCommand(2);
|
|
|
|
FillDrawCommand(cmd, rc);
|
|
|
|
cmd->palette.bits = 0;
|
|
|
|
|
|
|
|
if (rc.shading_enable)
|
2020-04-18 15:16:58 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->vertices[0].color = rc.color_for_first_vertex;
|
|
|
|
const GPUVertexPosition start_pos{FifoPop()};
|
|
|
|
cmd->vertices[0].x = m_drawing_offset.x + start_pos.x;
|
|
|
|
cmd->vertices[0].y = m_drawing_offset.y + start_pos.y;
|
|
|
|
|
|
|
|
cmd->vertices[1].color = FifoPop() & UINT32_C(0x00FFFFFF);
|
|
|
|
const GPUVertexPosition end_pos{FifoPop()};
|
|
|
|
cmd->vertices[1].x = m_drawing_offset.x + end_pos.x;
|
|
|
|
cmd->vertices[1].y = m_drawing_offset.y + end_pos.y;
|
2020-04-18 15:16:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->vertices[0].color = rc.color_for_first_vertex;
|
|
|
|
cmd->vertices[1].color = rc.color_for_first_vertex;
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
const GPUVertexPosition start_pos{FifoPop()};
|
|
|
|
cmd->vertices[0].x = m_drawing_offset.x + start_pos.x;
|
|
|
|
cmd->vertices[0].y = m_drawing_offset.y + start_pos.y;
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
const GPUVertexPosition end_pos{FifoPop()};
|
|
|
|
cmd->vertices[1].x = m_drawing_offset.x + end_pos.x;
|
|
|
|
cmd->vertices[1].y = m_drawing_offset.y + end_pos.y;
|
|
|
|
}
|
2019-12-22 09:34:24 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
if (!IsDrawingAreaIsValid())
|
|
|
|
return;
|
2020-05-14 15:31:48 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
const auto [min_x, max_x] = MinMax(cmd->vertices[0].x, cmd->vertices[1].x);
|
|
|
|
const auto [min_y, max_y] = MinMax(cmd->vertices[0].y, cmd->vertices[1].y);
|
|
|
|
if ((max_x - min_x) >= MAX_PRIMITIVE_WIDTH || (max_y - min_y) >= MAX_PRIMITIVE_HEIGHT)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG("Culling too-large line: {},{} - {},{}", cmd->vertices[0].y, cmd->vertices[0].y, cmd->vertices[1].x,
|
|
|
|
cmd->vertices[1].y);
|
2020-11-21 03:32:58 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2024-05-01 10:58:47 +00:00
|
|
|
AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
|
2019-10-26 02:57:35 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.PushCommand(cmd);
|
2019-12-22 09:34:24 +00:00
|
|
|
}
|
2020-09-20 11:33:24 +00:00
|
|
|
else
|
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
const u32 num_vertices = GetPolyLineVertexCount();
|
2020-09-20 11:33:24 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendDrawLineCommand* cmd = m_backend.NewDrawLineCommand(num_vertices);
|
|
|
|
FillDrawCommand(cmd, m_render_command);
|
2020-09-20 11:33:24 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
u32 buffer_pos = 0;
|
|
|
|
const GPUVertexPosition start_vp{m_blit_buffer[buffer_pos++]};
|
|
|
|
cmd->vertices[0].x = start_vp.x + m_drawing_offset.x;
|
|
|
|
cmd->vertices[0].y = start_vp.y + m_drawing_offset.y;
|
|
|
|
cmd->vertices[0].color = m_render_command.color_for_first_vertex;
|
2020-09-20 11:33:24 +00:00
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
const bool shaded = m_render_command.shading_enable;
|
|
|
|
for (u32 i = 1; i < num_vertices; i++)
|
2020-09-20 11:33:24 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->vertices[i].color =
|
|
|
|
shaded ? (m_blit_buffer[buffer_pos++] & UINT32_C(0x00FFFFFF)) : m_render_command.color_for_first_vertex;
|
|
|
|
const GPUVertexPosition vp{m_blit_buffer[buffer_pos++]};
|
|
|
|
cmd->vertices[i].x = m_drawing_offset.x + vp.x;
|
|
|
|
cmd->vertices[i].y = m_drawing_offset.y + vp.y;
|
|
|
|
|
2022-11-28 08:04:55 +00:00
|
|
|
const auto [min_x, max_x] = MinMax(cmd->vertices[i - 1].x, cmd->vertices[i].x);
|
|
|
|
const auto [min_y, max_y] = MinMax(cmd->vertices[i - 1].y, cmd->vertices[i].y);
|
2020-11-21 03:32:58 +00:00
|
|
|
if ((max_x - min_x) >= MAX_PRIMITIVE_WIDTH || (max_y - min_y) >= MAX_PRIMITIVE_HEIGHT)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG("Culling too-large line: {},{} - {},{}", cmd->vertices[i - 1].x, cmd->vertices[i - 1].y,
|
|
|
|
cmd->vertices[i].x, cmd->vertices[i].y);
|
2020-11-21 03:32:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-05-01 10:58:47 +00:00
|
|
|
AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
|
2020-11-21 03:32:58 +00:00
|
|
|
}
|
2020-09-20 11:33:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
m_backend.PushCommand(cmd);
|
2020-09-20 11:33:24 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-21 03:32:58 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UnreachableCode();
|
|
|
|
break;
|
2020-09-20 11:33:24 +00:00
|
|
|
}
|
2019-12-22 08:53:20 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
void GPU_SW::ReadVRAM(u32 x, u32 y, u32 width, u32 height)
|
2019-12-22 08:53:20 +00:00
|
|
|
{
|
2021-04-17 12:16:59 +00:00
|
|
|
m_backend.Sync(false);
|
2019-12-22 08:53:20 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
void GPU_SW::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
2020-09-20 11:33:24 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendFillVRAMCommand* cmd = m_backend.NewFillVRAMCommand();
|
|
|
|
FillBackendCommandParameters(cmd);
|
|
|
|
cmd->x = static_cast<u16>(x);
|
|
|
|
cmd->y = static_cast<u16>(y);
|
|
|
|
cmd->width = static_cast<u16>(width);
|
|
|
|
cmd->height = static_cast<u16>(height);
|
|
|
|
cmd->color = color;
|
|
|
|
m_backend.PushCommand(cmd);
|
2019-12-22 08:53:20 +00:00
|
|
|
}
|
|
|
|
|
2020-12-14 16:19:28 +00:00
|
|
|
void GPU_SW::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask)
|
2019-12-22 08:53:20 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
const u32 num_words = width * height;
|
|
|
|
GPUBackendUpdateVRAMCommand* cmd = m_backend.NewUpdateVRAMCommand(num_words);
|
|
|
|
FillBackendCommandParameters(cmd);
|
2020-12-14 16:19:28 +00:00
|
|
|
cmd->params.set_mask_while_drawing = set_mask;
|
|
|
|
cmd->params.check_mask_before_draw = check_mask;
|
2020-11-21 03:32:58 +00:00
|
|
|
cmd->x = static_cast<u16>(x);
|
|
|
|
cmd->y = static_cast<u16>(y);
|
|
|
|
cmd->width = static_cast<u16>(width);
|
|
|
|
cmd->height = static_cast<u16>(height);
|
|
|
|
std::memcpy(cmd->data, data, sizeof(u16) * num_words);
|
|
|
|
m_backend.PushCommand(cmd);
|
2019-12-22 08:53:20 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
void GPU_SW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height)
|
2019-12-22 09:34:24 +00:00
|
|
|
{
|
2020-11-21 03:32:58 +00:00
|
|
|
GPUBackendCopyVRAMCommand* cmd = m_backend.NewCopyVRAMCommand();
|
|
|
|
FillBackendCommandParameters(cmd);
|
|
|
|
cmd->src_x = static_cast<u16>(src_x);
|
|
|
|
cmd->src_y = static_cast<u16>(src_y);
|
|
|
|
cmd->dst_x = static_cast<u16>(dst_x);
|
|
|
|
cmd->dst_y = static_cast<u16>(dst_y);
|
|
|
|
cmd->width = static_cast<u16>(width);
|
|
|
|
cmd->height = static_cast<u16>(height);
|
|
|
|
m_backend.PushCommand(cmd);
|
2019-12-22 09:34:24 +00:00
|
|
|
}
|
|
|
|
|
2024-05-01 03:51:01 +00:00
|
|
|
void GPU_SW::FlushRender()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPU_SW::UpdateCLUT(GPUTexturePaletteReg reg, bool clut_is_8bit)
|
|
|
|
{
|
|
|
|
GPUBackendUpdateCLUTCommand* cmd = m_backend.NewUpdateCLUTCommand();
|
|
|
|
FillBackendCommandParameters(cmd);
|
|
|
|
cmd->reg.bits = reg.bits;
|
|
|
|
cmd->clut_is_8bit = clut_is_8bit;
|
|
|
|
m_backend.PushCommand(cmd);
|
|
|
|
}
|
|
|
|
|
2020-11-21 03:32:58 +00:00
|
|
|
std::unique_ptr<GPU> GPU::CreateSoftwareRenderer()
|
2019-12-22 09:34:24 +00:00
|
|
|
{
|
2023-01-28 11:38:03 +00:00
|
|
|
std::unique_ptr<GPU_SW> gpu(std::make_unique<GPU_SW>());
|
|
|
|
if (!gpu->Initialize())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return gpu;
|
2019-12-22 09:34:24 +00:00
|
|
|
}
|