GS: Remove exceptions

This commit is contained in:
Stenzek 2023-06-28 23:05:53 +10:00 committed by Connor McLaughlin
parent 81236209db
commit a889acb332
17 changed files with 395 additions and 561 deletions

View File

@ -342,52 +342,24 @@ void GSclose()
void GSreset(bool hardware_reset) void GSreset(bool hardware_reset)
{ {
try g_gs_renderer->Reset(hardware_reset);
{
g_gs_renderer->Reset(hardware_reset);
}
catch (GSRecoverableError)
{
}
} }
void GSgifSoftReset(u32 mask) void GSgifSoftReset(u32 mask)
{ {
try g_gs_renderer->SoftReset(mask);
{
g_gs_renderer->SoftReset(mask);
}
catch (GSRecoverableError)
{
}
} }
void GSwriteCSR(u32 csr) void GSwriteCSR(u32 csr)
{ {
try g_gs_renderer->WriteCSR(csr);
{
g_gs_renderer->WriteCSR(csr);
}
catch (GSRecoverableError)
{
}
} }
void GSInitAndReadFIFO(u8* mem, u32 size) void GSInitAndReadFIFO(u8* mem, u32 size)
{ {
GL_PERF("Init and read FIFO %u qwc", size); GL_PERF("Init and read FIFO %u qwc", size);
try g_gs_renderer->InitReadFIFO(mem, size);
{ g_gs_renderer->ReadFIFO(mem, size);
g_gs_renderer->InitReadFIFO(mem, size);
g_gs_renderer->ReadFIFO(mem, size);
}
catch (GSRecoverableError)
{
}
catch (const std::bad_alloc&)
{
fprintf(stderr, "GS: Memory allocation error\n");
}
} }
void GSReadLocalMemoryUnsync(u8* mem, u32 qwc, u64 BITBLITBUF, u64 TRXPOS, u64 TRXREG) void GSReadLocalMemoryUnsync(u8* mem, u32 qwc, u64 BITBLITBUF, u64 TRXPOS, u64 TRXREG)
@ -397,89 +369,47 @@ void GSReadLocalMemoryUnsync(u8* mem, u32 qwc, u64 BITBLITBUF, u64 TRXPOS, u64 T
void GSgifTransfer(const u8* mem, u32 size) void GSgifTransfer(const u8* mem, u32 size)
{ {
try g_gs_renderer->Transfer<3>(mem, size);
{
g_gs_renderer->Transfer<3>(mem, size);
}
catch (GSRecoverableError)
{
}
} }
void GSgifTransfer1(u8* mem, u32 addr) void GSgifTransfer1(u8* mem, u32 addr)
{ {
try g_gs_renderer->Transfer<0>(const_cast<u8*>(mem) + addr, (0x4000 - addr) / 16);
{
g_gs_renderer->Transfer<0>(const_cast<u8*>(mem) + addr, (0x4000 - addr) / 16);
}
catch (GSRecoverableError)
{
}
} }
void GSgifTransfer2(u8* mem, u32 size) void GSgifTransfer2(u8* mem, u32 size)
{ {
try g_gs_renderer->Transfer<1>(const_cast<u8*>(mem), size);
{
g_gs_renderer->Transfer<1>(const_cast<u8*>(mem), size);
}
catch (GSRecoverableError)
{
}
} }
void GSgifTransfer3(u8* mem, u32 size) void GSgifTransfer3(u8* mem, u32 size)
{ {
try g_gs_renderer->Transfer<2>(const_cast<u8*>(mem), size);
{
g_gs_renderer->Transfer<2>(const_cast<u8*>(mem), size);
}
catch (GSRecoverableError)
{
}
} }
void GSvsync(u32 field, bool registers_written) void GSvsync(u32 field, bool registers_written)
{ {
try g_gs_renderer->VSync(field, registers_written, g_gs_renderer->IsIdleFrame());
{
g_gs_renderer->VSync(field, registers_written, g_gs_renderer->IsIdleFrame());
}
catch (GSRecoverableError)
{
}
catch (const std::bad_alloc&)
{
fprintf(stderr, "GS: Memory allocation error\n");
}
} }
int GSfreeze(FreezeAction mode, freezeData* data) int GSfreeze(FreezeAction mode, freezeData* data)
{ {
try if (mode == FreezeAction::Save)
{ {
if (mode == FreezeAction::Save) return g_gs_renderer->Freeze(data, false);
{
return g_gs_renderer->Freeze(data, false);
}
else if (mode == FreezeAction::Size)
{
return g_gs_renderer->Freeze(data, true);
}
else if (mode == FreezeAction::Load)
{
// Since Defrost doesn't do a hardware reset (since it would be clearing
// local memory just before it's overwritten), we have to manually wipe
// out the current textures.
g_gs_device->ClearCurrent();
return g_gs_renderer->Defrost(data);
}
} }
catch (GSRecoverableError) else if (mode == FreezeAction::Size)
{ {
return g_gs_renderer->Freeze(data, true);
}
else // if (mode == FreezeAction::Load)
{
// Since Defrost doesn't do a hardware reset (since it would be clearing
// local memory just before it's overwritten), we have to manually wipe
// out the current textures.
g_gs_device->ClearCurrent();
return g_gs_renderer->Defrost(data);
} }
return 0;
} }
void GSQueueSnapshot(const std::string& path, u32 gsdump_frames) void GSQueueSnapshot(const std::string& path, u32 gsdump_frames)

View File

@ -119,13 +119,6 @@ bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspe
u32* width, u32* height, std::vector<u32>* pixels); u32* width, u32* height, std::vector<u32>* pixels);
void GSJoinSnapshotThreads(); void GSJoinSnapshotThreads();
struct GSError
{
};
struct GSRecoverableError : GSError
{
};
namespace Host namespace Host
{ {
/// Called when the GS is creating a render device. /// Called when the GS is creating a render device.

View File

@ -30,7 +30,7 @@ GSClut::GSClut(GSLocalMemory* mem)
// 1k + 1k for mirrored area simulating wrapping memory // 1k + 1k for mirrored area simulating wrapping memory
m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, VECTOR_ALIGNMENT)); m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, VECTOR_ALIGNMENT));
if (!m_clut) if (!m_clut)
throw std::bad_alloc(); pxFailRel("Failed to allocate CLUT storage.");
m_buff32 = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_clut) + 2048); // 1k m_buff32 = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_clut) + 2048); // 1k
m_buff64 = reinterpret_cast<u64*>(reinterpret_cast<u8*>(m_clut) + 4096); // 2k m_buff64 = reinterpret_cast<u64*>(reinterpret_cast<u8*>(m_clut) + 4096); // 2k

View File

@ -65,7 +65,7 @@ GSLocalMemory::GSLocalMemory()
{ {
m_vm8 = (u8*)GSAllocateWrappedMemory(m_vmsize, 4); m_vm8 = (u8*)GSAllocateWrappedMemory(m_vmsize, 4);
if (!m_vm8) if (!m_vm8)
throw std::bad_alloc(); pxFailRel("Failed to allocate GS memory storage.");
memset(m_vm8, 0, m_vmsize); memset(m_vm8, 0, m_vmsize);

View File

@ -269,8 +269,8 @@ void GSDumpLzma::Initialize()
if (ret != LZMA_OK) if (ret != LZMA_OK)
{ {
fprintf(stderr, "Error initializing the decoder! (error code %u)\n", ret); Console.Error("Error initializing the decoder! (error code %u)", ret);
throw "BAD"; // Just exit the program pxFailRel("Failed to initialize LZMA decoder.");
} }
m_buff_size = 1024*1024; m_buff_size = 1024*1024;
@ -301,8 +301,8 @@ void GSDumpLzma::Decompress()
if (ferror(m_fp)) if (ferror(m_fp))
{ {
fprintf(stderr, "Read error: %s\n", strerror(errno)); Console.Error("Read error: %s", strerror(errno));
throw "BAD"; // Just exit the program pxFailRel("LZMA read error.");
} }
} }
@ -310,12 +310,10 @@ void GSDumpLzma::Decompress()
if (ret != LZMA_OK) if (ret != LZMA_OK)
{ {
if (ret == LZMA_STREAM_END) if (ret != LZMA_STREAM_END)
fprintf(stderr, "LZMA decoder finished without error\n\n");
else
{ {
fprintf(stderr, "Decoder error: (error code %u)\n", ret); Console.Error("Decoder error: (error code %u)", ret);
throw "BAD"; // Just exit the program pxFailRel("LZMA decoder error.");
} }
} }
@ -395,16 +393,16 @@ void GSDumpDecompressZst::Decompress()
if (ferror(m_fp)) if (ferror(m_fp))
{ {
fprintf(stderr, "Read error: %s\n", strerror(errno)); Console.Error("Zst read error: %s", strerror(errno));
throw "BAD"; // Just exit the program pxFailRel("Zst read error.");
} }
} }
size_t ret = ZSTD_decompressStream(m_strm, &outbuf, &m_inbuf); size_t ret = ZSTD_decompressStream(m_strm, &outbuf, &m_inbuf);
if (ZSTD_isError(ret)) if (ZSTD_isError(ret))
{ {
fprintf(stderr, "Decoder error: (error code %s)\n", ZSTD_getErrorName(ret)); Console.Error("Decoder error: (error code %s)", ZSTD_getErrorName(ret));
throw "BAD"; // Just exit the program pxFailRel("Zst decoder error.");
} }
} }

View File

@ -58,53 +58,41 @@ namespace GSPng
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop info_ptr = nullptr; png_infop info_ptr = nullptr;
bool success; if (png_ptr == nullptr)
try return false;
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr)
return false;
if (setjmp(png_jmpbuf(png_ptr)))
return false;
png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr, compression);
png_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
if (channel_bit_depth > 8)
png_set_swap(png_ptr);
if (rb_swapped && type != PNG_COLOR_TYPE_GRAY)
png_set_bgr(png_ptr);
for (int y = 0; y < height; ++y)
{ {
if (png_ptr == nullptr) for (int x = 0; x < width; ++x)
throw GSRecoverableError(); for (int i = 0; i < bytes_per_pixel_out; ++i)
row[bytes_per_pixel_out * x + i] = image[y * pitch + bytes_per_pixel_in * x + i + offset];
info_ptr = png_create_info_struct(png_ptr); png_write_row(png_ptr, row);
if (info_ptr == nullptr)
throw GSRecoverableError();
if (setjmp(png_jmpbuf(png_ptr)))
throw GSRecoverableError();
png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr, compression);
png_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
if (channel_bit_depth > 8)
png_set_swap(png_ptr);
if (rb_swapped && type != PNG_COLOR_TYPE_GRAY)
png_set_bgr(png_ptr);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
for (int i = 0; i < bytes_per_pixel_out; ++i)
row[bytes_per_pixel_out * x + i] = image[y * pitch + bytes_per_pixel_in * x + i + offset];
png_write_row(png_ptr, row);
}
png_write_end(png_ptr, nullptr);
success = true;
}
catch (GSRecoverableError&)
{
fprintf(stderr, "Failed to write image %s\n", file.c_str());
success = false;
} }
png_write_end(png_ptr, nullptr);
if (png_ptr) if (png_ptr)
png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : nullptr); png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : nullptr);
fclose(fp); std::fclose(fp);
return success; return true;
} }
bool Save(GSPng::Format fmt, const std::string& file, const u8* image, int w, int h, int pitch, int compression, bool rb_swapped) bool Save(GSPng::Format fmt, const std::string& file, const u8* image, int w, int h, int pitch, int compression, bool rb_swapped)

View File

@ -1643,24 +1643,11 @@ void GSState::FlushPrim()
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
try // Skip draw if Z test is enabled, but set to fail all pixels.
{ const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER);
// Skip draw if Z test is enabled, but set to fail all pixels.
const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER);
if (!skip_draw) if (!skip_draw)
Draw(); Draw();
}
catch (GSRecoverableError&)
{
// could be an unsupported draw call
}
catch (const std::bad_alloc&)
{
// Texture Out Of Memory
PurgePool();
Console.Error("GS: Memory allocation failure.");
}
g_perfmon.Put(GSPerfMon::Draw, 1); g_perfmon.Put(GSPerfMon::Draw, 1);
g_perfmon.Put(GSPerfMon::Prim, m_index.tail / GSUtil::GetVertexCount(PRIM->PRIM)); g_perfmon.Put(GSPerfMon::Prim, m_index.tail / GSUtil::GetVertexCount(PRIM->PRIM));
@ -2706,8 +2693,7 @@ void GSState::GrowVertexBuffer()
Console.Error("GS: failed to allocate %zu bytes for vertices and %zu for indices.", Console.Error("GS: failed to allocate %zu bytes for vertices and %zu for indices.",
vert_byte_count, idx_byte_count); vert_byte_count, idx_byte_count);
pxFailRel("Memory allocation failed");
throw GSError();
} }
if (m_vertex.buff) if (m_vertex.buff)

View File

@ -306,7 +306,15 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
{ {
t = CreateSurface(type, width, height, levels, format); t = CreateSurface(type, width, height, levels, format);
if (!t) if (!t)
throw std::bad_alloc(); {
Console.Error("GS: Memory allocation failure for %dx%d texture. Purging pool and retrying.", width, height);
PurgePool();
if (!t)
{
Console.Error("GS: Memory allocation failure for %dx%d texture after purging pool.", width, height);
return nullptr;
}
}
} }
} }
@ -607,13 +615,9 @@ bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_con
return true; return true;
} }
GSTexture* new_tex; const GSTexture::Format fmt = orig_tex ? orig_tex->GetFormat() : GSTexture::Format::Color;
try GSTexture* new_tex = FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, fmt, !preserve_contents, true);
{ if (!new_tex)
const GSTexture::Format fmt = orig_tex ? orig_tex->GetFormat() : GSTexture::Format::Color;
new_tex = FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, fmt, !preserve_contents, true);
}
catch (std::bad_alloc&)
{ {
Console.WriteLn("%dx%d texture allocation failed in ResizeTexture()", w, h); Console.WriteLn("%dx%d texture allocation failed in ResizeTexture()", w, h);
return false; return false;

View File

@ -228,9 +228,7 @@ private:
void Grow() void Grow()
{ {
if (m_capacity == USHRT_MAX) if (m_capacity == USHRT_MAX)
{ pxFailRel("FastList size maxed out at USHRT_MAX (65535) elements, cannot grow futhermore.");
throw std::runtime_error("FastList size maxed out at USHRT_MAX (65535) elements, cannot grow futhermore.");
}
const u16 new_capacity = m_capacity <= (USHRT_MAX / 2) ? (m_capacity * 2) : USHRT_MAX; const u16 new_capacity = m_capacity <= (USHRT_MAX / 2) ? (m_capacity * 2) : USHRT_MAX;
@ -247,9 +245,7 @@ private:
// Initialize the additional space in the stack // Initialize the additional space in the stack
for (u16 i = m_capacity - 1; i < new_capacity - 1; i++) for (u16 i = m_capacity - 1; i < new_capacity - 1; i++)
{
m_free_indexes_stack[i] = i + 1; m_free_indexes_stack[i] = i + 1;
}
m_capacity = new_capacity; m_capacity = new_capacity;
} }

View File

@ -2212,6 +2212,9 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking) if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{ {
primid_tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false); primid_tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
if (!primid_tex)
return;
StretchRect(config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(), StretchRect(config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[config.datm].get(), nullptr, false); primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[config.datm].get(), nullptr, false);
} }
@ -2237,6 +2240,9 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.drawarea); const GSVector4 dRect(config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy(); const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor); hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor);
if (!hdr_rt)
return;
// Warning: StretchRect must be called before BeginScene otherwise // Warning: StretchRect must be called before BeginScene otherwise
// vertices will be overwritten. Trust me you don't want to do that. // vertices will be overwritten. Trust me you don't want to do that.
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false); StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);

View File

@ -2004,6 +2004,12 @@ void GSRendererHW::Draw()
const bool possible_shuffle = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 || IsPossibleChannelShuffle(); const bool possible_shuffle = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 || IsPossibleChannelShuffle();
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle) : src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle) :
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle); g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle);
if (unlikely(!src))
{
GL_INS("ERROR: Source lookup failed, skipping.");
cleanup_cancelled_draw();
return;
}
} }
// Estimate size based on the scissor rectangle and height cache. // Estimate size based on the scissor rectangle and height cache.
@ -2059,6 +2065,12 @@ void GSRendererHW::Draw()
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, target_scale, GSTextureCache::RenderTarget, true, rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preload_uploads); fm, false, force_preload, preload_uploads);
if (unlikely(!rt))
{
GL_INS("ERROR: Failed to create FRAME target, skipping.");
cleanup_cancelled_draw();
return;
}
} }
} }
@ -2077,6 +2089,12 @@ void GSRendererHW::Draw()
{ {
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil, ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, false, force_preload); m_cached_ctx.DepthWrite(), 0, false, force_preload);
if (unlikely(!ds))
{
GL_INS("ERROR: Failed to create ZBUF target, skipping.");
cleanup_cancelled_draw();
return;
}
} }
} }
@ -5168,23 +5186,10 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
// If some of the channels are masked, we need to keep them. // If some of the channels are masked, we need to keep them.
if (!clear_depth && m_cached_ctx.FRAME.FBMSK != 0) if (!clear_depth && m_cached_ctx.FRAME.FBMSK != 0)
{ {
GSTexture* tex = nullptr;
GSTextureCache::Target* target = clear_depth ? ds : rt; GSTextureCache::Target* target = clear_depth ? ds : rt;
const GSVector2 size = GSVector2(static_cast<float>(target->GetUnscaledWidth()) * target->m_scale, static_cast<float>(target->GetUnscaledHeight()) * target->m_scale); GSTexture* tex = g_gs_device->CreateRenderTarget(target->m_texture->GetWidth(), target->m_texture->GetHeight(), target->m_texture->GetFormat(), false);
pxAssert(!target->m_texture->IsDepthStencil());
try
{
tex = g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), false);
}
catch (const std::bad_alloc&)
{
}
if (!tex) if (!tex)
{
Console.Error("(ResizeTexture) Failed to allocate %dx%d texture", size.x, size.y);
return; return;
}
if (clear_depth) if (clear_depth)
{ {
@ -5240,22 +5245,10 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
// If some of the channels are masked, we need to keep them. // If some of the channels are masked, we need to keep them.
if (m_cached_ctx.FRAME.FBMSK != 0) if (m_cached_ctx.FRAME.FBMSK != 0)
{ {
GSTexture* tex = nullptr;
GSTextureCache::Target* target = rt; GSTextureCache::Target* target = rt;
const GSVector2 size = GSVector2(static_cast<float>(target->GetUnscaledWidth()) * target->m_scale, static_cast<float>(target->GetUnscaledHeight()) * target->m_scale); GSTexture* tex = g_gs_device->CreateRenderTarget(target->m_texture->GetWidth(), target->m_texture->GetHeight(), target->m_texture->GetFormat(), true);
try
{
tex = g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), true);
}
catch (const std::bad_alloc&)
{
}
if (!tex) if (!tex)
{
Console.Error("(ResizeTexture) Failed to allocate %dx%d texture", size.x, size.y);
return; return;
}
g_gs_device->ClearRenderTarget(tex, color); g_gs_device->ClearRenderTarget(tex, color);

View File

@ -579,7 +579,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
if (GSConfig.UserHacks_DisableDepthSupport) if (GSConfig.UserHacks_DisableDepthSupport)
{ {
GL_CACHE("LookupDepthSource not supported (0x%x, F:0x%x)", TEX0.TBP0, TEX0.PSM); GL_CACHE("LookupDepthSource not supported (0x%x, F:0x%x)", TEX0.TBP0, TEX0.PSM);
throw GSRecoverableError(); return nullptr;
} }
GL_CACHE("TC: Lookup Depth Source <%d,%d => %d,%d> (0x%x, %s, BW: %u, CBP: 0x%x, TW: %d, TH: %d)", r.x, r.y, r.z, GL_CACHE("TC: Lookup Depth Source <%d,%d => %d,%d> (0x%x, %s, BW: %u, CBP: 0x%x, TW: %d, TH: %d)", r.x, r.y, r.z,
@ -756,7 +756,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
if ((TEX0.TW > 10 && !region.HasX()) || (TEX0.TH > 10 && !region.HasY())) if ((TEX0.TW > 10 && !region.HasX()) || (TEX0.TH > 10 && !region.HasY()))
{ {
GL_CACHE("Invalid TEX0 size %ux%u without region, aborting draw.", TEX0.TW, TEX0.TH); GL_CACHE("Invalid TEX0 size %ux%u without region, aborting draw.", TEX0.TW, TEX0.TH);
throw GSRecoverableError(); return nullptr;
} }
const GSVector2i compare_lod(lod ? *lod : GSVector2i(0, 0)); const GSVector2i compare_lod(lod ? *lod : GSVector2i(0, 0));
@ -1382,7 +1382,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (dst_match) if (dst_match)
{ {
calcRescale(dst_match); calcRescale(dst_match);
dst = CreateTarget(TEX0, new_size.x, new_size.y, scale, type, clear); dst = Target::Create(TEX0, new_size.x, new_size.y, scale, type, clear);
if (!dst)
return nullptr;
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt; dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
dst->OffsetHack_modxy = dst_match->OffsetHack_modxy; dst->OffsetHack_modxy = dst_match->OffsetHack_modxy;
@ -1454,7 +1457,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
size.x, size.y, fbmask, bp, TEX0.TBW, psm_str(TEX0.PSM)); size.x, size.y, fbmask, bp, TEX0.TBW, psm_str(TEX0.PSM));
} }
Target* dst = CreateTarget(TEX0, size.x, size.y, scale, type, true); Target* dst = Target::Create(TEX0, size.x, size.y, scale, type, true);
// In theory new textures contain invalidated data. Still in theory a new target // In theory new textures contain invalidated data. Still in theory a new target
// must contains the content of the GS memory. // must contains the content of the GS memory.
// In practice, TC will wrongly invalidate some RT. For example due to write on the alpha // In practice, TC will wrongly invalidate some RT. For example due to write on the alpha
@ -2731,18 +2734,9 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// If the copies overlap, this is a validation error, so we need to copy to a temporary texture first. // If the copies overlap, this is a validation error, so we need to copy to a temporary texture first.
if ((SBP == DBP) && !(GSVector4i(sx, sy, sx + w, sy + h).rintersect(GSVector4i(dx, dy, dx + w, dy + h))).rempty()) if ((SBP == DBP) && !(GSVector4i(sx, sy, sx + w, sy + h).rintersect(GSVector4i(dx, dy, dx + w, dy + h))).rempty())
{ {
GSTexture* tmp_texture = nullptr; GSTexture* tmp_texture = src->m_texture->IsDepthStencil() ?
const GSVector4i src_size = GSVector4i(src->m_texture->GetSize()).xyxy(); g_gs_device->CreateDepthStencil(src->m_texture->GetWidth(), src->m_texture->GetHeight(), src->m_texture->GetFormat(), false) :
try g_gs_device->CreateRenderTarget(src->m_texture->GetWidth(), src->m_texture->GetHeight(), src->m_texture->GetFormat(), false);
{
tmp_texture = src->m_texture->IsDepthStencil() ?
g_gs_device->CreateDepthStencil(src_size.x, src_size.y, src->m_texture->GetFormat(), false) :
g_gs_device->CreateRenderTarget(src_size.x, src_size.y, src->m_texture->GetFormat(), false);
}
catch (const std::bad_alloc&)
{
}
if (!tmp_texture) if (!tmp_texture)
{ {
Console.Error("(HW Move) Failed to allocate temporary %dx%d texture on HW move", w, h); Console.Error("(HW Move) Failed to allocate temporary %dx%d texture on HW move", w, h);
@ -2752,7 +2746,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
if(tmp_texture->IsDepthStencil()) if(tmp_texture->IsDepthStencil())
{ {
const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h); const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h);
const GSVector4 tmp_rect = src_rect / GSVector4(src_size); const GSVector4 tmp_rect = src_rect / (GSVector4(tmp_texture->GetSize()).xyxy());
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h)); const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
g_gs_device->StretchRect(src->m_texture, tmp_rect, tmp_texture, src_rect, ShaderConvert::DEPTH_COPY, false); g_gs_device->StretchRect(src->m_texture, tmp_rect, tmp_texture, src_rect, ShaderConvert::DEPTH_COPY, false);
g_gs_device->StretchRect(tmp_texture, tmp_rect, dst->m_texture, dst_rect, ShaderConvert::DEPTH_COPY, false); g_gs_device->StretchRect(tmp_texture, tmp_rect, dst->m_texture, dst_rect, ShaderConvert::DEPTH_COPY, false);
@ -4050,35 +4044,25 @@ void GSTextureCache::AgeHashCache()
} }
} }
GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int w, int h, float scale, int type, const bool clear) GSTextureCache::Target* GSTextureCache::Target::Create(GIFRegTEX0 TEX0, int w, int h, float scale, int type, bool clear)
{ {
ASSERT(type == RenderTarget || type == DepthStencil); ASSERT(type == RenderTarget || type == DepthStencil);
const int scaled_w = static_cast<int>(std::ceil(static_cast<float>(w) * scale)); const int scaled_w = static_cast<int>(std::ceil(static_cast<float>(w) * scale));
const int scaled_h = static_cast<int>(std::ceil(static_cast<float>(h) * scale)); const int scaled_h = static_cast<int>(std::ceil(static_cast<float>(h) * scale));
GSTexture* texture = (type == RenderTarget) ?
g_gs_device->CreateRenderTarget(scaled_w, scaled_h, GSTexture::Format::Color, clear) :
g_gs_device->CreateDepthStencil(scaled_w, scaled_h, GSTexture::Format::DepthStencil, clear);
if (!texture)
return nullptr;
// TODO: This leaks if memory allocation fails. Use a unique_ptr so it gets freed, but these Target* t = new Target(TEX0, type, GSVector2i(w, h), scale, texture);
// exceptions really need to get lost.
std::unique_ptr<Target> t = std::make_unique<Target>(TEX0, type);
t->m_unscaled_size = GSVector2i(w, h);
t->m_scale = scale;
if (type == RenderTarget) g_texture_cache->m_target_memory_usage += t->m_texture->GetMemUsage();
{
t->m_texture = g_gs_device->CreateRenderTarget(scaled_w, scaled_h, GSTexture::Format::Color, clear);
t->m_used = true; // FIXME g_texture_cache->m_dst[type].push_front(t);
}
else if (type == DepthStencil)
{
t->m_texture = g_gs_device->CreateDepthStencil(scaled_w, scaled_h, GSTexture::Format::DepthStencil, clear);
}
m_target_memory_usage += t->m_texture->GetMemUsage(); return t;
m_dst[type].push_front(t.get());
return t.release();
} }
GSTexture* GSTextureCache::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size) GSTexture* GSTextureCache::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size)
@ -4646,12 +4630,15 @@ bool GSTextureCache::Source::ClutMatch(const PaletteKey& palette_key)
// GSTextureCache::Target // GSTextureCache::Target
GSTextureCache::Target::Target(const GIFRegTEX0& TEX0, const int type) GSTextureCache::Target::Target(GIFRegTEX0 TEX0, int type, const GSVector2i& unscaled_size, float scale, GSTexture* texture)
: m_type(type) : m_type(type)
, m_used(false) , m_used(type == RenderTarget) // FIXME
, m_valid(GSVector4i::zero()) , m_valid(GSVector4i::zero())
{ {
m_TEX0 = TEX0; m_TEX0 = TEX0;
m_unscaled_size = unscaled_size;
m_scale = scale;
m_texture = texture;
m_32_bits_fmt |= (GSLocalMemory::m_psm[TEX0.PSM].trbpp != 16); m_32_bits_fmt |= (GSLocalMemory::m_psm[TEX0.PSM].trbpp != 16);
} }
@ -4876,19 +4863,9 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
const int new_height = static_cast<int>(std::ceil(new_unscaled_height) * m_scale); const int new_height = static_cast<int>(std::ceil(new_unscaled_height) * m_scale);
const bool clear = (new_width > width || new_height > height); const bool clear = (new_width > width || new_height > height);
// These exceptions *really* need to get lost. This gets called outside of draws, which just crashes GSTexture* tex = m_texture->IsDepthStencil() ?
// when it tries to propagate the exception back. g_gs_device->CreateDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) :
GSTexture* tex = nullptr; g_gs_device->CreateRenderTarget(new_width, new_height, m_texture->GetFormat(), clear);
try
{
tex = m_texture->IsDepthStencil() ?
g_gs_device->CreateDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) :
g_gs_device->CreateRenderTarget(new_width, new_height, m_texture->GetFormat(), clear);
}
catch (const std::bad_alloc&)
{
}
if (!tex) if (!tex)
{ {
Console.Error("(ResizeTexture) Failed to allocate %dx%d texture from %dx%d texture", new_width, new_height, width, height); Console.Error("(ResizeTexture) Failed to allocate %dx%d texture from %dx%d texture", new_width, new_height, width, height);

View File

@ -227,9 +227,11 @@ public:
int readbacks_since_draw = 0; int readbacks_since_draw = 0;
public: public:
Target(const GIFRegTEX0& TEX0, const int type); Target(GIFRegTEX0 TEX0, int type, const GSVector2i& unscaled_size, float scale, GSTexture* texture);
~Target(); ~Target();
static Target* Create(GIFRegTEX0 TEX0, int w, int h, float scale, int type, bool clear);
void ResizeDrawn(const GSVector4i& rect); void ResizeDrawn(const GSVector4i& rect);
void UpdateDrawn(const GSVector4i& rect, bool can_resize = true); void UpdateDrawn(const GSVector4i& rect, bool can_resize = true);
void ResizeValidity(const GSVector4i& rect); void ResizeValidity(const GSVector4i& rect);
@ -406,7 +408,6 @@ protected:
std::unique_ptr<GSDownloadTexture> m_uint32_download_texture; std::unique_ptr<GSDownloadTexture> m_uint32_download_texture;
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region); Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, float scale, int type, const bool clear);
/// Expands a target when the block pointer for a display framebuffer is within another target, but the read offset /// Expands a target when the block pointer for a display framebuffer is within another target, but the read offset
/// plus the height is larger than the current size of the target. /// plus the height is larger than the current size of the target.

View File

@ -673,7 +673,7 @@ MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
{ {
NSString* msg = [NSString stringWithFormat:@"Failed to load shader %@: %@", name, [err localizedDescription]]; NSString* msg = [NSString stringWithFormat:@"Failed to load shader %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]); Console.Error("%s", [msg UTF8String]);
throw GSRecoverableError(); pxFailRel("Failed to load shader, check the log for more details.");
} }
return fn; return fn;
} }
@ -689,7 +689,7 @@ MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipeline
{ {
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]]; NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]); Console.Error("%s", [msg UTF8String]);
throw GSRecoverableError(); pxFailRel("Failed to create pipeline, check the log for more details.");
} }
return res; return res;
} }
@ -709,7 +709,7 @@ MRCOwned<id<MTLComputePipelineState>> GSDeviceMTL::MakeComputePipeline(id<MTLFun
{ {
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]]; NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]); Console.Error("%s", [msg UTF8String]);
throw GSRecoverableError(); pxFailRel("Failed to create pipeline, check the log for more details.");
} }
return res; return res;
} }
@ -914,269 +914,262 @@ bool GSDeviceMTL::Create()
m_features.cas_sharpening = true; m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true; m_features.test_and_sample_depth = true;
try // Init metal stuff
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
m_spin_fence = MRCTransfer([m_dev.dev newFence]);
[m_spin_fence setLabel:@"Spin Fence"];
constexpr MTLResourceOptions spin_opts = MTLResourceStorageModePrivate | MTLResourceHazardTrackingModeUntracked;
m_spin_buffer = MRCTransfer([m_dev.dev newBufferWithLength:4 options:spin_opts]);
[m_spin_buffer setLabel:@"Spin Buffer"];
id<MTLCommandBuffer> initCommands = [m_queue commandBuffer];
id<MTLBlitCommandEncoder> clearSpinBuffer = [initCommands blitCommandEncoder];
[clearSpinBuffer fillBuffer:m_spin_buffer range:NSMakeRange(0, 4) value:0];
[clearSpinBuffer updateFence:m_spin_fence];
[clearSpinBuffer endEncoding];
m_spin_pipeline = MakeComputePipeline(LoadShader(@"waste_time"), @"waste_time");
for (int sharpen_only = 0; sharpen_only < 2; sharpen_only++)
{ {
// Init metal stuff setFnConstantB(m_fn_constants, sharpen_only, GSMTLConstantIndex_CAS_SHARPEN_ONLY);
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]); NSString* shader = m_dev.features.has_fast_half ? @"CASHalf" : @"CASFloat";
setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH); m_cas_pipeline[sharpen_only] = MakeComputePipeline(LoadShader(shader), sharpen_only ? @"CAS Sharpen" : @"CAS Upscale");
}
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]); m_expand_index_buffer = CreatePrivateBufferWithContent(m_dev.dev, initCommands, MTLResourceHazardTrackingModeUntracked, EXPAND_BUFFER_SIZE, GenerateExpansionIndexBuffer);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"]; [m_expand_index_buffer setLabel:@"Point/Sprite Expand Indices"];
m_spin_fence = MRCTransfer([m_dev.dev newFence]);
[m_spin_fence setLabel:@"Spin Fence"];
constexpr MTLResourceOptions spin_opts = MTLResourceStorageModePrivate | MTLResourceHazardTrackingModeUntracked;
m_spin_buffer = MRCTransfer([m_dev.dev newBufferWithLength:4 options:spin_opts]);
[m_spin_buffer setLabel:@"Spin Buffer"];
id<MTLCommandBuffer> initCommands = [m_queue commandBuffer];
id<MTLBlitCommandEncoder> clearSpinBuffer = [initCommands blitCommandEncoder];
[clearSpinBuffer fillBuffer:m_spin_buffer range:NSMakeRange(0, 4) value:0];
[clearSpinBuffer updateFence:m_spin_fence];
[clearSpinBuffer endEncoding];
m_spin_pipeline = MakeComputePipeline(LoadShader(@"waste_time"), @"waste_time");
for (int sharpen_only = 0; sharpen_only < 2; sharpen_only++) m_hw_vertex = MRCTransfer([MTLVertexDescriptor new]);
[[[m_hw_vertex layouts] objectAtIndexedSubscript:GSMTLBufferIndexHWVertices] setStride:sizeof(GSVertex)];
applyAttribute(m_hw_vertex, GSMTLAttributeIndexST, MTLVertexFormatFloat2, offsetof(GSVertex, ST), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexC, MTLVertexFormatUChar4, offsetof(GSVertex, RGBAQ.R), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexQ, MTLVertexFormatFloat, offsetof(GSVertex, RGBAQ.Q), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexXY, MTLVertexFormatUShort2, offsetof(GSVertex, XYZ.X), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexZ, MTLVertexFormatUInt, offsetof(GSVertex, XYZ.Z), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexUV, MTLVertexFormatUShort2, offsetof(GSVertex, UV), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexF, MTLVertexFormatUChar4Normalized, offsetof(GSVertex, FOG), GSMTLBufferIndexHWVertices);
for (auto& desc : m_render_pass_desc)
{
desc = MRCTransfer([MTLRenderPassDescriptor new]);
[[desc depthAttachment] setStoreAction:MTLStoreActionStore];
[[desc stencilAttachment] setStoreAction:MTLStoreActionStore];
}
// Init samplers
m_sampler_hw[SamplerSelector::Linear().key] = CreateSampler(m_dev.dev, SamplerSelector::Linear());
m_sampler_hw[SamplerSelector::Point().key] = CreateSampler(m_dev.dev, SamplerSelector::Point());
// Init depth stencil states
MTLDepthStencilDescriptor* dssdesc = [[MTLDepthStencilDescriptor new] autorelease];
MTLStencilDescriptor* stencildesc = [[MTLStencilDescriptor new] autorelease];
stencildesc.stencilCompareFunction = MTLCompareFunctionAlways;
stencildesc.depthFailureOperation = MTLStencilOperationKeep;
stencildesc.stencilFailureOperation = MTLStencilOperationKeep;
stencildesc.depthStencilPassOperation = MTLStencilOperationReplace;
dssdesc.frontFaceStencil = stencildesc;
dssdesc.backFaceStencil = stencildesc;
[dssdesc setLabel:@"Stencil Write"];
m_dss_stencil_write = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
dssdesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationZero;
dssdesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationZero;
[dssdesc setLabel:@"Stencil Zero"];
m_dss_stencil_zero = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
stencildesc.stencilCompareFunction = MTLCompareFunctionEqual;
stencildesc.readMask = 1;
stencildesc.writeMask = 1;
for (size_t i = 0; i < std::size(m_dss_hw); i++)
{
GSHWDrawConfig::DepthStencilSelector sel;
sel.key = i;
if (sel.date)
{ {
setFnConstantB(m_fn_constants, sharpen_only, GSMTLConstantIndex_CAS_SHARPEN_ONLY); if (sel.date_one)
NSString* shader = m_dev.features.has_fast_half ? @"CASHalf" : @"CASFloat"; stencildesc.depthStencilPassOperation = MTLStencilOperationZero;
m_cas_pipeline[sharpen_only] = MakeComputePipeline(LoadShader(shader), sharpen_only ? @"CAS Sharpen" : @"CAS Upscale");
}
m_expand_index_buffer = CreatePrivateBufferWithContent(m_dev.dev, initCommands, MTLResourceHazardTrackingModeUntracked, EXPAND_BUFFER_SIZE, GenerateExpansionIndexBuffer);
[m_expand_index_buffer setLabel:@"Point/Sprite Expand Indices"];
m_hw_vertex = MRCTransfer([MTLVertexDescriptor new]);
[[[m_hw_vertex layouts] objectAtIndexedSubscript:GSMTLBufferIndexHWVertices] setStride:sizeof(GSVertex)];
applyAttribute(m_hw_vertex, GSMTLAttributeIndexST, MTLVertexFormatFloat2, offsetof(GSVertex, ST), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexC, MTLVertexFormatUChar4, offsetof(GSVertex, RGBAQ.R), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexQ, MTLVertexFormatFloat, offsetof(GSVertex, RGBAQ.Q), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexXY, MTLVertexFormatUShort2, offsetof(GSVertex, XYZ.X), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexZ, MTLVertexFormatUInt, offsetof(GSVertex, XYZ.Z), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexUV, MTLVertexFormatUShort2, offsetof(GSVertex, UV), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexF, MTLVertexFormatUChar4Normalized, offsetof(GSVertex, FOG), GSMTLBufferIndexHWVertices);
for (auto& desc : m_render_pass_desc)
{
desc = MRCTransfer([MTLRenderPassDescriptor new]);
[[desc depthAttachment] setStoreAction:MTLStoreActionStore];
[[desc stencilAttachment] setStoreAction:MTLStoreActionStore];
}
// Init samplers
m_sampler_hw[SamplerSelector::Linear().key] = CreateSampler(m_dev.dev, SamplerSelector::Linear());
m_sampler_hw[SamplerSelector::Point().key] = CreateSampler(m_dev.dev, SamplerSelector::Point());
// Init depth stencil states
MTLDepthStencilDescriptor* dssdesc = [[MTLDepthStencilDescriptor new] autorelease];
MTLStencilDescriptor* stencildesc = [[MTLStencilDescriptor new] autorelease];
stencildesc.stencilCompareFunction = MTLCompareFunctionAlways;
stencildesc.depthFailureOperation = MTLStencilOperationKeep;
stencildesc.stencilFailureOperation = MTLStencilOperationKeep;
stencildesc.depthStencilPassOperation = MTLStencilOperationReplace;
dssdesc.frontFaceStencil = stencildesc;
dssdesc.backFaceStencil = stencildesc;
[dssdesc setLabel:@"Stencil Write"];
m_dss_stencil_write = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
dssdesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationZero;
dssdesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationZero;
[dssdesc setLabel:@"Stencil Zero"];
m_dss_stencil_zero = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
stencildesc.stencilCompareFunction = MTLCompareFunctionEqual;
stencildesc.readMask = 1;
stencildesc.writeMask = 1;
for (size_t i = 0; i < std::size(m_dss_hw); i++)
{
GSHWDrawConfig::DepthStencilSelector sel;
sel.key = i;
if (sel.date)
{
if (sel.date_one)
stencildesc.depthStencilPassOperation = MTLStencilOperationZero;
else
stencildesc.depthStencilPassOperation = MTLStencilOperationKeep;
dssdesc.frontFaceStencil = stencildesc;
dssdesc.backFaceStencil = stencildesc;
}
else else
{ stencildesc.depthStencilPassOperation = MTLStencilOperationKeep;
dssdesc.frontFaceStencil = nil; dssdesc.frontFaceStencil = stencildesc;
dssdesc.backFaceStencil = nil; dssdesc.backFaceStencil = stencildesc;
}
dssdesc.depthWriteEnabled = sel.zwe ? YES : NO;
static constexpr MTLCompareFunction ztst[] =
{
MTLCompareFunctionNever,
MTLCompareFunctionAlways,
MTLCompareFunctionGreaterEqual,
MTLCompareFunctionGreater,
};
static constexpr const char* ztstname[] =
{
"DepthNever",
"DepthAlways",
"DepthGEq",
"DepthEq",
};
const char* datedesc = sel.date ? (sel.date_one ? " DATE_ONE" : " DATE") : "";
const char* zwedesc = sel.zwe ? " ZWE" : "";
dssdesc.depthCompareFunction = ztst[sel.ztst];
[dssdesc setLabel:[NSString stringWithFormat:@"%s%s%s", ztstname[sel.ztst], zwedesc, datedesc]];
m_dss_hw[i] = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
} }
else
// Init HW Vertex Shaders
for (size_t i = 0; i < std::size(m_hw_vs); i++)
{ {
VSSelector sel; dssdesc.frontFaceStencil = nil;
sel.key = i; dssdesc.backFaceStencil = nil;
if (sel.point_size && sel.expand != GSMTLExpandType::None)
continue;
setFnConstantB(m_fn_constants, sel.fst, GSMTLConstantIndex_FST);
setFnConstantB(m_fn_constants, sel.iip, GSMTLConstantIndex_IIP);
setFnConstantB(m_fn_constants, sel.point_size, GSMTLConstantIndex_VS_POINT_SIZE);
NSString* shader = @"vs_main";
if (sel.expand != GSMTLExpandType::None)
{
setFnConstantI(m_fn_constants, static_cast<u32>(sel.expand), GSMTLConstantIndex_VS_EXPAND_TYPE);
shader = @"vs_main_expand";
}
m_hw_vs[i] = LoadShader(shader);
} }
dssdesc.depthWriteEnabled = sel.zwe ? YES : NO;
// Init pipelines static constexpr MTLCompareFunction ztst[] =
auto vs_convert = LoadShader(@"vs_convert");
auto fs_triangle = LoadShader(@"fs_triangle");
auto ps_copy = LoadShader(@"ps_copy");
auto pdesc = [[MTLRenderPipelineDescriptor new] autorelease];
// FS Triangle Pipelines
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
m_hdr_resolve_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_resolve"), @"HDR Resolve");
m_fxaa_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_fxaa"), @"fxaa");
m_shadeboost_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_shadeboost"), @"shadeboost");
m_clut_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_4"), @"4-bit CLUT Update");
m_clut_pipeline[1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_8"), @"8-bit CLUT Update");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::HDRColor);
m_hdr_init_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_init"), @"HDR Init");
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatInvalid;
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
m_datm_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_datm0"), @"datm0");
m_datm_pipeline[1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_datm1"), @"datm1");
m_stencil_clear_pipeline = MakePipeline(pdesc, fs_triangle, nil, @"Stencil Clear");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::PrimID);
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
pdesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
m_primid_init_pipeline[1][0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm0"), @"PrimID DATM0 Clear");
m_primid_init_pipeline[1][1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm1"), @"PrimID DATM1 Clear");
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
m_primid_init_pipeline[0][0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm0"), @"PrimID DATM0 Clear");
m_primid_init_pipeline[0][1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm1"), @"PrimID DATM1 Clear");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
applyAttribute(pdesc.vertexDescriptor, 0, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, pos), 0);
applyAttribute(pdesc.vertexDescriptor, 1, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, texpos), 0);
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ConvertShaderVertex);
for (size_t i = 0; i < std::size(m_interlace_pipeline); i++)
{ {
NSString* name = [NSString stringWithFormat:@"ps_interlace%zu", i]; MTLCompareFunctionNever,
m_interlace_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), name); MTLCompareFunctionAlways,
} MTLCompareFunctionGreaterEqual,
for (size_t i = 0; i < std::size(m_convert_pipeline); i++) MTLCompareFunctionGreater,
};
static constexpr const char* ztstname[] =
{ {
ShaderConvert conv = static_cast<ShaderConvert>(i); "DepthNever",
NSString* name = [NSString stringWithCString:shaderName(conv) encoding:NSUTF8StringEncoding]; "DepthAlways",
switch (conv) "DepthGEq",
{ "DepthEq",
case ShaderConvert::Count: };
case ShaderConvert::DATM_0: const char* datedesc = sel.date ? (sel.date_one ? " DATE_ONE" : " DATE") : "";
case ShaderConvert::DATM_1: const char* zwedesc = sel.zwe ? " ZWE" : "";
case ShaderConvert::CLUT_4: dssdesc.depthCompareFunction = ztst[sel.ztst];
case ShaderConvert::CLUT_8: [dssdesc setLabel:[NSString stringWithFormat:@"%s%s%s", ztstname[sel.ztst], zwedesc, datedesc]];
case ShaderConvert::HDR_INIT: m_dss_hw[i] = MRCTransfer([m_dev.dev newDepthStencilStateWithDescriptor:dssdesc]);
case ShaderConvert::HDR_RESOLVE:
continue;
case ShaderConvert::FLOAT32_TO_32_BITS:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::UInt32);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
case ShaderConvert::FLOAT32_TO_16_BITS:
case ShaderConvert::RGBA8_TO_16_BITS:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::UInt16);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
case ShaderConvert::DEPTH_COPY:
case ShaderConvert::RGBA8_TO_FLOAT32:
case ShaderConvert::RGBA8_TO_FLOAT24:
case ShaderConvert::RGBA8_TO_FLOAT16:
case ShaderConvert::RGB5A1_TO_FLOAT16:
case ShaderConvert::RGBA8_TO_FLOAT32_BILN:
case ShaderConvert::RGBA8_TO_FLOAT24_BILN:
case ShaderConvert::RGBA8_TO_FLOAT16_BILN:
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN:
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatInvalid;
pdesc.depthAttachmentPixelFormat = ConvertPixelFormat(GSTexture::Format::DepthStencil);
break;
case ShaderConvert::COPY:
case ShaderConvert::RGBA_TO_8I: // Yes really
case ShaderConvert::TRANSPARENCY_FILTER:
case ShaderConvert::FLOAT32_TO_RGBA8:
case ShaderConvert::FLOAT16_TO_RGB5A1:
case ShaderConvert::YUV:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
}
m_convert_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), name);
}
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
for (size_t i = 0; i < std::size(m_present_pipeline); i++)
{
PresentShader conv = static_cast<PresentShader>(i);
NSString* name = [NSString stringWithCString:shaderName(conv) encoding:NSUTF8StringEncoding];
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
m_present_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), [NSString stringWithFormat:@"present_%s", shaderName(conv) + 3]);
}
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
for (size_t i = 0; i < std::size(m_convert_pipeline_copy_mask); i++)
{
MTLColorWriteMask mask = MTLColorWriteMaskNone;
if (i & 1) mask |= MTLColorWriteMaskRed;
if (i & 2) mask |= MTLColorWriteMaskGreen;
if (i & 4) mask |= MTLColorWriteMaskBlue;
if (i & 8) mask |= MTLColorWriteMaskAlpha;
NSString* name = [NSString stringWithFormat:@"copy_%s%s%s%s", i & 1 ? "r" : "", i & 2 ? "g" : "", i & 4 ? "b" : "", i & 8 ? "a" : ""];
pdesc.colorAttachments[0].writeMask = mask;
m_convert_pipeline_copy_mask[i] = MakePipeline(pdesc, vs_convert, ps_copy, name);
}
pdesc.colorAttachments[0].blendingEnabled = YES;
pdesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pdesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pdesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
for (size_t i = 0; i < std::size(m_merge_pipeline); i++)
{
bool mmod = i & 1;
bool amod = i & 2;
NSString* name = [NSString stringWithFormat:@"ps_merge%zu", mmod];
NSString* pipename = [NSString stringWithFormat:@"Merge%s%s", mmod ? " MMOD" : "", amod ? " AMOD" : ""];
pdesc.colorAttachments[0].writeMask = amod ? MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue : MTLColorWriteMaskAll;
m_merge_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), pipename);
}
pdesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
applyAttribute(pdesc.vertexDescriptor, 0, MTLVertexFormatFloat2, offsetof(ImDrawVert, pos), 0);
applyAttribute(pdesc.vertexDescriptor, 1, MTLVertexFormatFloat2, offsetof(ImDrawVert, uv), 0);
applyAttribute(pdesc.vertexDescriptor, 2, MTLVertexFormatUChar4Normalized, offsetof(ImDrawVert, col), 0);
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
m_imgui_pipeline = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui"), @"imgui");
[initCommands commit];
} }
catch (GSRecoverableError&)
// Init HW Vertex Shaders
for (size_t i = 0; i < std::size(m_hw_vs); i++)
{ {
return false; VSSelector sel;
sel.key = i;
if (sel.point_size && sel.expand != GSMTLExpandType::None)
continue;
setFnConstantB(m_fn_constants, sel.fst, GSMTLConstantIndex_FST);
setFnConstantB(m_fn_constants, sel.iip, GSMTLConstantIndex_IIP);
setFnConstantB(m_fn_constants, sel.point_size, GSMTLConstantIndex_VS_POINT_SIZE);
NSString* shader = @"vs_main";
if (sel.expand != GSMTLExpandType::None)
{
setFnConstantI(m_fn_constants, static_cast<u32>(sel.expand), GSMTLConstantIndex_VS_EXPAND_TYPE);
shader = @"vs_main_expand";
}
m_hw_vs[i] = LoadShader(shader);
} }
// Init pipelines
auto vs_convert = LoadShader(@"vs_convert");
auto fs_triangle = LoadShader(@"fs_triangle");
auto ps_copy = LoadShader(@"ps_copy");
auto pdesc = [[MTLRenderPipelineDescriptor new] autorelease];
// FS Triangle Pipelines
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
m_hdr_resolve_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_resolve"), @"HDR Resolve");
m_fxaa_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_fxaa"), @"fxaa");
m_shadeboost_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_shadeboost"), @"shadeboost");
m_clut_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_4"), @"4-bit CLUT Update");
m_clut_pipeline[1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_8"), @"8-bit CLUT Update");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::HDRColor);
m_hdr_init_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_init"), @"HDR Init");
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatInvalid;
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
m_datm_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_datm0"), @"datm0");
m_datm_pipeline[1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_datm1"), @"datm1");
m_stencil_clear_pipeline = MakePipeline(pdesc, fs_triangle, nil, @"Stencil Clear");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::PrimID);
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
pdesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
m_primid_init_pipeline[1][0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm0"), @"PrimID DATM0 Clear");
m_primid_init_pipeline[1][1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm1"), @"PrimID DATM1 Clear");
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
m_primid_init_pipeline[0][0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm0"), @"PrimID DATM0 Clear");
m_primid_init_pipeline[0][1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_init_datm1"), @"PrimID DATM1 Clear");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
applyAttribute(pdesc.vertexDescriptor, 0, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, pos), 0);
applyAttribute(pdesc.vertexDescriptor, 1, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, texpos), 0);
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ConvertShaderVertex);
for (size_t i = 0; i < std::size(m_interlace_pipeline); i++)
{
NSString* name = [NSString stringWithFormat:@"ps_interlace%zu", i];
m_interlace_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), name);
}
for (size_t i = 0; i < std::size(m_convert_pipeline); i++)
{
ShaderConvert conv = static_cast<ShaderConvert>(i);
NSString* name = [NSString stringWithCString:shaderName(conv) encoding:NSUTF8StringEncoding];
switch (conv)
{
case ShaderConvert::Count:
case ShaderConvert::DATM_0:
case ShaderConvert::DATM_1:
case ShaderConvert::CLUT_4:
case ShaderConvert::CLUT_8:
case ShaderConvert::HDR_INIT:
case ShaderConvert::HDR_RESOLVE:
continue;
case ShaderConvert::FLOAT32_TO_32_BITS:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::UInt32);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
case ShaderConvert::FLOAT32_TO_16_BITS:
case ShaderConvert::RGBA8_TO_16_BITS:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::UInt16);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
case ShaderConvert::DEPTH_COPY:
case ShaderConvert::RGBA8_TO_FLOAT32:
case ShaderConvert::RGBA8_TO_FLOAT24:
case ShaderConvert::RGBA8_TO_FLOAT16:
case ShaderConvert::RGB5A1_TO_FLOAT16:
case ShaderConvert::RGBA8_TO_FLOAT32_BILN:
case ShaderConvert::RGBA8_TO_FLOAT24_BILN:
case ShaderConvert::RGBA8_TO_FLOAT16_BILN:
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN:
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatInvalid;
pdesc.depthAttachmentPixelFormat = ConvertPixelFormat(GSTexture::Format::DepthStencil);
break;
case ShaderConvert::COPY:
case ShaderConvert::RGBA_TO_8I: // Yes really
case ShaderConvert::TRANSPARENCY_FILTER:
case ShaderConvert::FLOAT32_TO_RGBA8:
case ShaderConvert::FLOAT16_TO_RGB5A1:
case ShaderConvert::YUV:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
break;
}
m_convert_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), name);
}
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
for (size_t i = 0; i < std::size(m_present_pipeline); i++)
{
PresentShader conv = static_cast<PresentShader>(i);
NSString* name = [NSString stringWithCString:shaderName(conv) encoding:NSUTF8StringEncoding];
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
m_present_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), [NSString stringWithFormat:@"present_%s", shaderName(conv) + 3]);
}
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
for (size_t i = 0; i < std::size(m_convert_pipeline_copy_mask); i++)
{
MTLColorWriteMask mask = MTLColorWriteMaskNone;
if (i & 1) mask |= MTLColorWriteMaskRed;
if (i & 2) mask |= MTLColorWriteMaskGreen;
if (i & 4) mask |= MTLColorWriteMaskBlue;
if (i & 8) mask |= MTLColorWriteMaskAlpha;
NSString* name = [NSString stringWithFormat:@"copy_%s%s%s%s", i & 1 ? "r" : "", i & 2 ? "g" : "", i & 4 ? "b" : "", i & 8 ? "a" : ""];
pdesc.colorAttachments[0].writeMask = mask;
m_convert_pipeline_copy_mask[i] = MakePipeline(pdesc, vs_convert, ps_copy, name);
}
pdesc.colorAttachments[0].blendingEnabled = YES;
pdesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pdesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pdesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
for (size_t i = 0; i < std::size(m_merge_pipeline); i++)
{
bool mmod = i & 1;
bool amod = i & 2;
NSString* name = [NSString stringWithFormat:@"ps_merge%zu", mmod];
NSString* pipename = [NSString stringWithFormat:@"Merge%s%s", mmod ? " MMOD" : "", amod ? " AMOD" : ""];
pdesc.colorAttachments[0].writeMask = amod ? MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue : MTLColorWriteMaskAll;
m_merge_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), pipename);
}
pdesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
applyAttribute(pdesc.vertexDescriptor, 0, MTLVertexFormatFloat2, offsetof(ImDrawVert, pos), 0);
applyAttribute(pdesc.vertexDescriptor, 1, MTLVertexFormatFloat2, offsetof(ImDrawVert, uv), 0);
applyAttribute(pdesc.vertexDescriptor, 2, MTLVertexFormatUChar4Normalized, offsetof(ImDrawVert, col), 0);
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
m_imgui_pipeline = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui"), @"imgui");
[initCommands commit];
return true; return true;
}} }}

View File

@ -1066,6 +1066,8 @@ GSTexture* GSDeviceOGL::InitPrimDateTexture(GSTexture* rt, const GSVector4i& are
const GSVector2i& rtsize = rt->GetSize(); const GSVector2i& rtsize = rt->GetSize();
GSTexture* tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false); GSTexture* tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
if (!tex)
return nullptr;
GL_PUSH("PrimID Destination Alpha Clear"); GL_PUSH("PrimID Destination Alpha Clear");
StretchRect(rt, GSVector4(area) / GSVector4(rtsize).xyxy(), tex, GSVector4(area), m_date.primid_ps[datm], false); StretchRect(rt, GSVector4(area) / GSVector4(rtsize).xyxy(), tex, GSVector4(area), m_date.primid_ps[datm], false);

View File

@ -20,6 +20,7 @@
#include "xbyak/xbyak.h" #include "xbyak/xbyak.h"
#include "xbyak/xbyak_util.h" #include "xbyak/xbyak_util.h"
#include "GS/MultiISA.h" #include "GS/MultiISA.h"
#include "common/Assertions.h"
/// Code generator that automatically selects between SSE and AVX, x86 and x64 so you don't have to /// Code generator that automatically selects between SSE and AVX, x86 and x64 so you don't have to
/// Should make combined SSE and AVX codegen much easier /// Should make combined SSE and AVX codegen much easier
@ -38,45 +39,11 @@ public:
using Ymm = Xbyak::Ymm; using Ymm = Xbyak::Ymm;
using Zmm = Xbyak::Zmm; using Zmm = Xbyak::Zmm;
class Error : public std::exception
{
public:
enum Value
{
ERR_64_BIT_REG_IN_32,
ERR_64_INSTR_IN_32,
ERR_SSE_INSTR_IN_AVX,
ERR_AVX_INSTR_IN_SSE,
};
Value value;
Error(Value value) : value(value) {}
const char* what() const noexcept
{
static const char* tbl[] = {
"used 64-bit register in 32-bit code",
"used 64-bit only instruction in 32-bit code",
"used SSE instruction in AVX code",
"used AVX instruction in SSE code",
};
if (static_cast<u32>(value) < (sizeof(tbl) / sizeof(*tbl)))
{
return tbl[value];
}
else
{
return "GSNewCodeGenerator Unknown Error";
}
}
};
private: private:
void requireAVX() void requireAVX()
{ {
if (!hasAVX) if (!hasAVX)
throw Error(Error::ERR_AVX_INSTR_IN_SSE); pxFailRel("used AVX instruction in SSE code");
} }
public: public:
@ -141,7 +108,7 @@ public:
#define ACTUAL_FORWARD_SSEONLY(name, ...) \ #define ACTUAL_FORWARD_SSEONLY(name, ...) \
if (hasAVX) \ if (hasAVX) \
throw Error(Error::ERR_SSE_INSTR_IN_AVX); \ pxFailRel("used SSE instruction in AVX code"); \
else \ else \
actual.name(__VA_ARGS__); actual.name(__VA_ARGS__);
@ -149,19 +116,19 @@ public:
if (hasAVX) \ if (hasAVX) \
actual.name(__VA_ARGS__); \ actual.name(__VA_ARGS__); \
else \ else \
throw Error(Error::ERR_AVX_INSTR_IN_SSE); pxFailRel("used AVX instruction in SSE code");
#define ACTUAL_FORWARD_AVX2(name, ...) \ #define ACTUAL_FORWARD_AVX2(name, ...) \
if (hasAVX2) \ if (hasAVX2) \
actual.name(__VA_ARGS__); \ actual.name(__VA_ARGS__); \
else \ else \
throw Error(Error::ERR_AVX_INSTR_IN_SSE); pxFailRel("used AVX instruction in SSE code");
#define ACTUAL_FORWARD_FMA(name, ...) \ #define ACTUAL_FORWARD_FMA(name, ...) \
if (hasFMA) \ if (hasFMA) \
actual.name(__VA_ARGS__); \ actual.name(__VA_ARGS__); \
else \ else \
throw Error(Error::ERR_AVX_INSTR_IN_SSE); pxFailRel("used AVX instruction in SSE code");
#define FORWARD1(category, name, type) \ #define FORWARD1(category, name, type) \
void name(type a) \ void name(type a) \

View File

@ -59,7 +59,7 @@ GSRasterizer::GSRasterizer(GSDrawScanline* ds, int id, int threads)
m_edge.buff = static_cast<GSVertexSW*>(_aligned_malloc(sizeof(GSVertexSW) * 2048, VECTOR_ALIGNMENT)); m_edge.buff = static_cast<GSVertexSW*>(_aligned_malloc(sizeof(GSVertexSW) * 2048, VECTOR_ALIGNMENT));
m_edge.count = 0; m_edge.count = 0;
if (!m_edge.buff) if (!m_edge.buff)
throw std::bad_alloc(); pxFailRel("failed to allocate storage for m_edge.buff");
int rows = (2048 >> m_thread_height) + 16; int rows = (2048 >> m_thread_height) + 16;
m_scanline = (u8*)_aligned_malloc(rows, 64); m_scanline = (u8*)_aligned_malloc(rows, 64);