#include "Cafe/HW/Latte/Core/LatteBufferCache.h" #include "Cafe/HW/Latte/Core/LattePM4.h" #include "Cafe/OS/common/OSCommon.h" #include "GX2.h" #include "GX2_Command.h" #include "GX2_Resource.h" #include "GX2_Streamout.h" #include "GX2_Draw.h" namespace GX2 { MPTR GX2RAllocateFunc = MPTR_NULL; MPTR GX2RFreeFunc = MPTR_NULL; void GX2RSetAllocator(MPTR funcAllocMPTR, MPTR funcFreeMPR) { GX2RAllocateFunc = funcAllocMPTR; GX2RFreeFunc = funcFreeMPR; } uint32 GX2RGetBufferAllocationSize(GX2RBuffer* buffer) { return (buffer->GetSize() + 0x3F) & ~0x3F; // pad to 64 byte alignment } uint32 GX2RGetBufferAlignment(uint32 resFlags) { if ((resFlags & GX2R_RESFLAG_USAGE_STREAM_OUTPUT) != 0) return 0x100; if ((resFlags & GX2R_RESFLAG_USAGE_UNIFORM_BLOCK) != 0) return 0x100; if ((resFlags & GX2R_RESFLAG_USAGE_SHADER_PROGRAM) != 0) return 0x100; if ((resFlags & GX2R_RESFLAG_USAGE_GS_RINGBUFFER) != 0) return 0x100; if ((resFlags & GX2R_RESFLAG_USAGE_VERTEX_BUFFER) != 0) return 0x40; if ((resFlags & GX2R_RESFLAG_USAGE_INDEX_BUFFER) != 0) return 0x40; if ((resFlags & GX2R_RESFLAG_USAGE_DISPLAY_LIST) != 0) return 0x40; return 0x100; } bool GX2RCreateBuffer(GX2RBuffer* buffer) { uint32 bufferAlignment = GX2RGetBufferAlignment(buffer->resFlags); uint32 bufferSize = GX2RGetBufferAllocationSize(buffer); MPTR allocResult = PPCCoreCallback(GX2RAllocateFunc, (uint32)buffer->resFlags, bufferSize, bufferAlignment); buffer->ptr = allocResult; buffer->resFlags &= ~GX2R_RESFLAG_LOCKED; buffer->resFlags |= GX2R_RESFLAG_ALLOCATED_BY_GX2R; // todo: invalidation return allocResult != MPTR_NULL; } bool GX2RCreateBufferUserMemory(GX2RBuffer* buffer, void* ptr, uint32 unusedSizeParameter) { buffer->ptr = ptr; buffer->resFlags &= ~GX2R_RESFLAG_LOCKED; buffer->resFlags &= ~GX2R_RESFLAG_ALLOCATED_BY_GX2R; // todo: invalidation return true; } void GX2RDestroyBufferEx(GX2RBuffer* buffer, uint32 resFlags) { if ((buffer->resFlags & GX2R_RESFLAG_ALLOCATED_BY_GX2R) == 0) { // this buffer is user-allocated buffer->ptr = nullptr; return; } PPCCoreCallback(GX2RFreeFunc, (uint32)buffer->resFlags, buffer->GetPtr()); buffer->ptr = nullptr; } bool GX2RBufferExists(GX2RBuffer* buffer) { if (!buffer) return false; if (!buffer->GetPtr()) return false; return true; } void GX2RSetBufferName(GX2RBuffer* buffer, const char* name) { // no-op in production builds } void* GX2RLockBufferEx(GX2RBuffer* buffer, uint32 resFlags) { return buffer->GetPtr(); } void GX2RUnlockBufferEx(GX2RBuffer* buffer, uint32 resFlags) { // todo - account for flags, not all buffer types need flushing LatteBufferCache_notifyDCFlush(buffer->GetVirtualAddr(), buffer->GetSize()); } void GX2RInvalidateBuffer(GX2RBuffer* buffer, uint32 resFlags) { // todo - account for flags, not all buffer types need flushing LatteBufferCache_notifyDCFlush(buffer->GetVirtualAddr(), buffer->GetSize()); } void GX2RSetAttributeBuffer(GX2RBuffer* buffer, uint32 bufferIndex, uint32 stride, uint32 offset) { uint32 bufferSize = buffer->GetSize(); if (offset > bufferSize) cemuLog_log(LogType::Force, "GX2RSetAttributeBuffer(): Offset exceeds buffer size"); GX2SetAttribBuffer(bufferIndex, bufferSize - offset, stride, ((uint8be*)buffer->GetPtr()) + offset); } void GX2RSetStreamOutBuffer(uint32 bufferIndex, GX2StreamOutBuffer* soBuffer) { // seen in CoD: Ghosts and CoD: Black Ops 2 GX2SetStreamOutBuffer(bufferIndex, soBuffer); } bool GX2RCreateSurface(GX2Surface* surface, uint32 resFlags) { // seen in Transformers Prime surface->resFlag = resFlags; GX2CalcSurfaceSizeAndAlignment(surface); surface->resFlag &= ~GX2R_RESFLAG_LOCKED; surface->resFlag |= GX2R_RESFLAG_ALLOCATED_BY_GX2R; MPTR allocResult = PPCCoreCallback(GX2RAllocateFunc, (uint32)surface->resFlag, (uint32)surface->imageSize + (uint32)surface->mipSize, (uint32)surface->alignment); surface->imagePtr = allocResult; if (surface->imagePtr != MPTR_NULL && surface->mipSize > 0) { surface->mipPtr = (uint32)surface->imagePtr + surface->imageSize; } else { surface->mipPtr = MPTR_NULL; } // todo: Cache invalidation based on resourceFlags? return allocResult != MPTR_NULL; } bool GX2RCreateSurfaceUserMemory(GX2Surface* surface, void* imagePtr, void* mipPtr, uint32 resFlags) { surface->resFlag = resFlags; surface->resFlag &= ~(GX2R_RESFLAG_LOCKED | GX2R_RESFLAG_ALLOCATED_BY_GX2R); GX2CalcSurfaceSizeAndAlignment(surface); surface->imagePtr = memory_getVirtualOffsetFromPointer(imagePtr); surface->mipPtr = memory_getVirtualOffsetFromPointer(mipPtr); if (surface->resFlag & 0x14000) { // memory invalidate } return true; } void GX2RDestroySurfaceEx(GX2Surface* surface, uint32 resFlags) { if ((surface->resFlag & GX2R_RESFLAG_ALLOCATED_BY_GX2R) == 0) { // this surface is user-allocated surface->imagePtr = MPTR_NULL; return; } resFlags &= (GX2R_RESFLAG_UKN_BIT_19 | GX2R_RESFLAG_UKN_BIT_20 | GX2R_RESFLAG_UKN_BIT_21 | GX2R_RESFLAG_UKN_BIT_22 | GX2R_RESFLAG_UKN_BIT_23); PPCCoreCallback(GX2RFreeFunc, (uint32)surface->resFlag | resFlags, (uint32)surface->imagePtr); surface->imagePtr = MPTR_NULL; } bool GX2RSurfaceExists(GX2Surface* surface) { if (!surface) return false; if (surface->imagePtr == MPTR_NULL) return false; if ((surface->resFlag & (GX2R_RESFLAG_USAGE_CPU_READ | GX2R_RESFLAG_USAGE_CPU_WRITE | GX2R_RESFLAG_USAGE_GPU_READ | GX2R_RESFLAG_USAGE_GPU_WRITE)) == 0) return false; return true; } void* GX2RLockSurfaceEx(GX2Surface* surface, uint32 mipLevel, uint32 resFlags) { // todo: handle invalidation surface->resFlag |= GX2R_RESFLAG_LOCKED; return memory_getPointerFromVirtualOffset(surface->imagePtr); } void GX2RUnlockSurfaceEx(GX2Surface* surface, uint32 mipLevel, uint32 resFlags) { // todo: handle invalidation surface->resFlag &= ~GX2R_RESFLAG_LOCKED; } void GX2RBeginDisplayListEx(GX2RBuffer* buffer, bool ukn, uint32 resFlags) { // todo: handle invalidation GX2::GX2BeginDisplayList(buffer->GetPtr(), buffer->GetSize()); } uint32 GX2REndDisplayList(GX2RBuffer* buffer) { return GX2::GX2EndDisplayList(buffer->GetPtr()); } void GX2RCallDisplayList(GX2RBuffer* buffer, uint32 size) { GX2::GX2CallDisplayList(buffer->GetVirtualAddr(), size); } void GX2RDirectCallDisplayList(GX2RBuffer* buffer, uint32 size) { GX2::GX2DirectCallDisplayList(buffer->GetPtr(), size); } void GX2RDrawIndexed(GX2PrimitiveMode2 primitiveMode, GX2RBuffer* indexBuffer, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE indexType, uint32 count, uint32 baseIndex, uint32 baseVertex, uint32 numInstances) { GX2DrawIndexedEx(primitiveMode, count, indexType, (uint8be*)indexBuffer->GetPtr() + (baseIndex * (uint32)indexBuffer->elementSize), baseVertex, numInstances); } void GX2ResourceInit() { cafeExportRegister("gx2", GX2RSetAllocator, LogType::GX2); cafeExportRegister("gx2", GX2RGetBufferAllocationSize, LogType::GX2); cafeExportRegister("gx2", GX2RGetBufferAlignment, LogType::GX2); cafeExportRegister("gx2", GX2RCreateBuffer, LogType::GX2); cafeExportRegister("gx2", GX2RCreateBufferUserMemory, LogType::GX2); cafeExportRegister("gx2", GX2RDestroyBufferEx, LogType::GX2); cafeExportRegister("gx2", GX2RBufferExists, LogType::GX2); cafeExportRegister("gx2", GX2RSetBufferName, LogType::GX2); cafeExportRegister("gx2", GX2RLockBufferEx, LogType::GX2); cafeExportRegister("gx2", GX2RUnlockBufferEx, LogType::GX2); cafeExportRegister("gx2", GX2RInvalidateBuffer, LogType::GX2); cafeExportRegister("gx2", GX2RSetAttributeBuffer, LogType::GX2); cafeExportRegister("gx2", GX2RSetStreamOutBuffer, LogType::GX2); cafeExportRegister("gx2", GX2RCreateSurface, LogType::GX2); cafeExportRegister("gx2", GX2RCreateSurfaceUserMemory, LogType::GX2); cafeExportRegister("gx2", GX2RDestroySurfaceEx, LogType::GX2); cafeExportRegister("gx2", GX2RSurfaceExists, LogType::GX2); cafeExportRegister("gx2", GX2RLockSurfaceEx, LogType::GX2); cafeExportRegister("gx2", GX2RUnlockSurfaceEx, LogType::GX2); cafeExportRegister("gx2", GX2RBeginDisplayListEx, LogType::GX2); cafeExportRegister("gx2", GX2REndDisplayList, LogType::GX2); cafeExportRegister("gx2", GX2RCallDisplayList, LogType::GX2); cafeExportRegister("gx2", GX2RDirectCallDisplayList, LogType::GX2); cafeExportRegister("gx2", GX2RDrawIndexed, LogType::GX2); GX2RAllocateFunc = MPTR_NULL; GX2RFreeFunc = MPTR_NULL; } };