diff --git a/CMakeLists.txt b/CMakeLists.txt index e29cc4db..3dd31fd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ add_library(miniwin STATIC EXCLUDE_FROM_ALL miniwin/miniwin/src/miniwin_d3drm.cpp ) # Force reported render mods from MiniWin -target_compile_definitions(miniwin PRIVATE MINIWIN_PIXELFORMAT=SDL_PIXELFORMAT_INDEX8) +target_compile_definitions(miniwin PRIVATE MINIWIN_PIXELFORMAT=SDL_PIXELFORMAT_RGB565) target_include_directories(miniwin PUBLIC "$") target_include_directories(miniwin PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/miniwin/miniwin/src/include") target_compile_definitions(miniwin PUBLIC "MINIWIN") diff --git a/miniwin/miniwin/include/miniwin_ddraw.h b/miniwin/miniwin/include/miniwin_ddraw.h index bc57b33f..a6dd97cc 100644 --- a/miniwin/miniwin/include/miniwin_ddraw.h +++ b/miniwin/miniwin/include/miniwin_ddraw.h @@ -104,6 +104,7 @@ DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0, 0x2B43, 0x11CF, 0xA2, 0xDE, 0x00, 0xAA DEFINE_GUID(IID_IDirectDrawSurface3, 0xDA044E00, 0x69B2, 0x11D0, 0xA1, 0xD5, 0x00, 0xAA, 0x00, 0xB8, 0xDF, 0xBB); extern SDL_Window* DDWindow; +extern SDL_Surface* DDBackBuffer; // --- Enums --- #define DDCKEY_SRCBLT DDColorKeyFlags::SRCBLT diff --git a/miniwin/miniwin/src/miniwin_d3drm.cpp b/miniwin/miniwin/src/miniwin_d3drm.cpp index 6bd62a08..7c08b166 100644 --- a/miniwin/miniwin/src/miniwin_d3drm.cpp +++ b/miniwin/miniwin/src/miniwin_d3drm.cpp @@ -7,6 +7,11 @@ #include #include +typedef struct PositionColorVertex { + float x, y, z; + Uint8 r, g, b, a; +} PositionColorVertex; + template class Direct3DRMArrayBase : public ArrayInterface { public: @@ -257,13 +262,239 @@ struct Direct3DRMTextureImpl : public Direct3DRMObjectBase HRESULT Changed(BOOL pixels, BOOL palette) override { return DD_OK; } }; +SDL_GPUShader* LoadShader( + SDL_GPUDevice* device, + const char* shaderFilename, + Uint32 samplerCount, + Uint32 uniformBufferCount, + Uint32 storageBufferCount, + Uint32 storageTextureCount +) +{ + const char* basePath = SDL_GetBasePath(); + if (!basePath) { + SDL_Log("Failed to get base path."); + return NULL; + } + + // Detect shader stage based on filename extension + SDL_GPUShaderStage stage; + if (SDL_strstr(shaderFilename, ".vert")) { + stage = SDL_GPU_SHADERSTAGE_VERTEX; + } + else if (SDL_strstr(shaderFilename, ".frag")) { + stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + } + else { + SDL_Log("Invalid shader stage: %s", shaderFilename); + return NULL; + } + + char fullPath[256]; + SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(device); + SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_INVALID; + const char* entrypoint = "main"; + + if (formats & SDL_GPU_SHADERFORMAT_SPIRV) { + SDL_snprintf(fullPath, sizeof(fullPath), "%sShaders/Compiled/SPIRV/%s.spv", basePath, shaderFilename); + format = SDL_GPU_SHADERFORMAT_SPIRV; + } + else if (formats & SDL_GPU_SHADERFORMAT_MSL) { + SDL_snprintf(fullPath, sizeof(fullPath), "%sShaders/Compiled/MSL/%s.msl", basePath, shaderFilename); + format = SDL_GPU_SHADERFORMAT_MSL; + entrypoint = "main0"; + } + else if (formats & SDL_GPU_SHADERFORMAT_DXIL) { + SDL_snprintf(fullPath, sizeof(fullPath), "%sShaders/Compiled/DXIL/%s.dxil", basePath, shaderFilename); + format = SDL_GPU_SHADERFORMAT_DXIL; + } + else { + SDL_Log("Unsupported backend shader format."); + return NULL; + } + + size_t codeSize; + void* code = SDL_LoadFile(fullPath, &codeSize); + if (!code) { + SDL_Log("Failed to load shader file: %s", fullPath); + return NULL; + } + + SDL_GPUShaderCreateInfo shaderInfo = { + codeSize, + (const Uint8*) code, + entrypoint, + format, + stage, + samplerCount, + storageTextureCount, + storageBufferCount, + uniformBufferCount, + }; + + SDL_GPUShader* shader = SDL_CreateGPUShader(device, &shaderInfo); + if (!shader) { + SDL_Log("Shader creation failed."); + } + + SDL_free(code); + return shader; +} + +SDL_GPUDevice* m_device; +SDL_GPUGraphicsPipeline* m_pipeline; +SDL_GPUTexture* m_transferTexture; +SDL_GPUTransferBuffer* m_downloadTransferBuffer; +SDL_GPUBuffer* VertexBuffer; +int m_vertexCount = 3; + struct Direct3DRMDevice2Impl : public Direct3DRMObjectBase { Direct3DRMDevice2Impl() { + m_device = SDL_CreateGPUDevice( + SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, + true, + NULL + ); + if (m_device == NULL) { + SDL_Log("GPUCreateDevice failed"); + return; + } + if (DDWindow == NULL) { + SDL_Log("CreateWindow failed: %s", SDL_GetError()); + return; + } + if (!SDL_ClaimWindowForGPUDevice(m_device, DDWindow)) { + SDL_Log("GPUClaimWindow failed"); + return; + } + SDL_GPUShader* vertexShader = LoadShader(m_device, "PositionColor.vert", 0, 0, 0, 0); + if (vertexShader == NULL) { + SDL_Log("Failed to create vertex shader!"); + return; + } + SDL_GPUShader* fragmentShader = LoadShader(m_device, "SolidColor.frag", 0, 0, 0, 0); + if (fragmentShader == NULL) { + SDL_Log("Failed to create fragment shader!"); + return; + } + + SDL_GPUColorTargetDescription colorTargets = {SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB}; + SDL_GPUVertexBufferDescription vertexBufferDescs[] = { + {.slot = 0, + .pitch = sizeof(PositionColorVertex), + .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX, + .instance_step_rate = 0} + }; + + SDL_GPUVertexAttribute vertexAttrs[] = { + {.location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3, .offset = 0}, + {.location = 1, + .buffer_slot = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, + .offset = sizeof(float) * 3} + }; + + SDL_GPUVertexInputState vertexInputState = { + .vertex_buffer_descriptions = vertexBufferDescs, + .num_vertex_buffers = 1, + .vertex_attributes = vertexAttrs, + .num_vertex_attributes = 2 + }; + + SDL_GPUGraphicsPipelineCreateInfo pipelineCreateInfo = + {.vertex_shader = vertexShader, + .fragment_shader = fragmentShader, + .vertex_input_state = vertexInputState, + .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .target_info = { + .color_target_descriptions = &colorTargets, + .num_color_targets = 1, + }}; + + m_pipeline = SDL_CreateGPUGraphicsPipeline(m_device, &pipelineCreateInfo); + if (m_pipeline == NULL) { + SDL_Log("Failed to create fill pipeline!"); + return; + } + + // Clean up shader resources + SDL_ReleaseGPUShader(m_device, vertexShader); + SDL_ReleaseGPUShader(m_device, fragmentShader); + // Create the vertex buffer + SDL_GPUBufferCreateInfo bufferCreateInfo = { + .usage = SDL_GPU_BUFFERUSAGE_VERTEX, + .size = (Uint32) (sizeof(PositionColorVertex) * m_vertexCount) + }; + + VertexBuffer = SDL_CreateGPUBuffer(m_device, &bufferCreateInfo); + + SDL_GPUTransferBufferCreateInfo transferCreateInfo = { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = (Uint32) (sizeof(PositionColorVertex) * m_vertexCount) + }; + + SDL_GPUTransferBuffer* transferBuffer = SDL_CreateGPUTransferBuffer(m_device, &transferCreateInfo); + + PositionColorVertex* transferData = + (PositionColorVertex*) SDL_MapGPUTransferBuffer(m_device, transferBuffer, false); + + transferData[0] = (PositionColorVertex){-1, -1, 0, 255, 0, 0, 255}; + transferData[1] = (PositionColorVertex){1, -1, 0, 0, 0, 255, 255}; + transferData[2] = (PositionColorVertex){0, 1, 0, 0, 255, 0, 128}; + + SDL_UnmapGPUTransferBuffer(m_device, transferBuffer); + + // Upload the transfer data to the vertex buffer + SDL_GPUCommandBuffer* uploadCmdBuf = SDL_AcquireGPUCommandBuffer(m_device); + SDL_GPUCopyPass* copyPass = SDL_BeginGPUCopyPass(uploadCmdBuf); + + SDL_GPUTransferBufferLocation transferLocation = {.transfer_buffer = transferBuffer, .offset = 0}; + + SDL_GPUBufferRegion bufferRegion = + {.buffer = VertexBuffer, .offset = 0, .size = (Uint32) (sizeof(PositionColorVertex) * m_vertexCount)}; + + SDL_UploadToGPUBuffer(copyPass, &transferLocation, &bufferRegion, false); + + SDL_EndGPUCopyPass(copyPass); + SDL_SubmitGPUCommandBuffer(uploadCmdBuf); + SDL_ReleaseGPUTransferBuffer(m_device, transferBuffer); + + SDL_GPUTextureCreateInfo textureInfo = {}; + textureInfo.type = SDL_GPU_TEXTURETYPE_2D; + textureInfo.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB; + textureInfo.width = 640; + textureInfo.height = 480; + textureInfo.layer_count_or_depth = 1; + textureInfo.num_levels = 1; + textureInfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + m_transferTexture = SDL_CreateGPUTexture(m_device, &textureInfo); + if (m_transferTexture == NULL) { + SDL_Log("Failed to create fill pipeline!"); + return; + } + SDL_GPUTransferBufferCreateInfo downloadTransferInfo = { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD, + .size = 640 * 480 * 4 + }; + m_downloadTransferBuffer = SDL_CreateGPUTransferBuffer(m_device, &downloadTransferInfo); + if (!m_downloadTransferBuffer) { + return; + } + m_viewports = new Direct3DRMViewportArrayImpl; m_viewports->AddRef(); } - ~Direct3DRMDevice2Impl() override { m_viewports->Release(); } + ~Direct3DRMDevice2Impl() override + { + m_viewports->Release(); + + SDL_ReleaseGPUGraphicsPipeline(m_device, m_pipeline); + SDL_ReleaseWindowFromGPUDevice(m_device, DDWindow); + SDL_ReleaseGPUBuffer(m_device, VertexBuffer); + SDL_DestroyWindow(DDWindow); + SDL_DestroyGPUDevice(m_device); + } unsigned int GetWidth() override { return 640; } unsigned int GetHeight() override { return 480; } HRESULT SetBufferCount(int count) override { return DD_OK; } @@ -370,7 +601,93 @@ struct Direct3DRMFrameImpl : public Direct3DRMObjectBase { }; struct Direct3DRMViewportImpl : public Direct3DRMObjectBase { - HRESULT Render(IDirect3DRMFrame* group) override { return DD_OK; } + HRESULT Render(IDirect3DRMFrame* group) override + { + if (!m_transferTexture || !DDBackBuffer) { + return DDERR_GENERIC; + } + SDL_GPUCommandBuffer* cmdbuf = SDL_AcquireGPUCommandBuffer(m_device); + if (cmdbuf == NULL) { + return DDERR_GENERIC; + } + + // Render the graphics + SDL_GPUColorTargetInfo colorTargetInfo = {}; + colorTargetInfo.texture = m_transferTexture; + // Make the render target transparent so we can combine it with the back buffer + colorTargetInfo.clear_color = {0, 0, 0, 0}; + colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; + + SDL_GPURenderPass* renderPass = SDL_BeginGPURenderPass(cmdbuf, &colorTargetInfo, 1, NULL); + SDL_BindGPUGraphicsPipeline(renderPass, m_pipeline); + SDL_GPUBufferBinding vertexBufferBinding = {.buffer = VertexBuffer, .offset = 0}; + SDL_BindGPUVertexBuffers(renderPass, 0, &vertexBufferBinding, 1); + SDL_DrawGPUPrimitives(renderPass, m_vertexCount, 1, 0, 0); + SDL_EndGPURenderPass(renderPass); + + // Download rendered image + SDL_GPUCopyPass* copyPass = SDL_BeginGPUCopyPass(cmdbuf); + SDL_GPUTextureRegion region = {}; + region.texture = m_transferTexture; + region.w = DDBackBuffer->w; + region.h = DDBackBuffer->h; + region.d = 1; + SDL_GPUTextureTransferInfo transferInfo = {}; + transferInfo.transfer_buffer = m_downloadTransferBuffer; + transferInfo.offset = 0; + SDL_DownloadFromGPUTexture(copyPass, ®ion, &transferInfo); + SDL_EndGPUCopyPass(copyPass); + SDL_GPUFence* fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmdbuf); + if (!cmdbuf || !SDL_WaitForGPUFences(m_device, true, &fence, 1)) { + return DDERR_GENERIC; + } + SDL_ReleaseGPUFence(m_device, fence); + void* downloadedData = SDL_MapGPUTransferBuffer(m_device, m_downloadTransferBuffer, false); + if (!downloadedData) { + return DDERR_GENERIC; + } + SDL_Surface* renderedImage = SDL_CreateSurfaceFrom( + DDBackBuffer->w, + DDBackBuffer->h, + SDL_PIXELFORMAT_ABGR8888, + downloadedData, + DDBackBuffer->w * 4 + ); + if (!renderedImage) { + return DDERR_GENERIC; + } + + // Blit the render back to our backbuffer + SDL_Rect srcRect{0, 0, DDBackBuffer->w, DDBackBuffer->h}; + + if (renderedImage->format == DDBackBuffer->format) { + // No conversion needed + SDL_BlitSurface(renderedImage, &srcRect, DDBackBuffer, &srcRect); + SDL_DestroySurface(renderedImage); + return DD_OK; + } + + const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(DDBackBuffer->format); + if (details->Amask != 0) { + // Backbuffer supports transparnacy + SDL_Surface* convertedRender = SDL_ConvertSurface(renderedImage, DDBackBuffer->format); + SDL_DestroySurface(renderedImage); + SDL_BlitSurface(convertedRender, &srcRect, DDBackBuffer, &srcRect); + SDL_DestroySurface(convertedRender); + return DD_OK; + } + + // Convert backbuffer to a format that supports transparancy + SDL_Surface* tempBackbuffer = SDL_ConvertSurface(DDBackBuffer, renderedImage->format); + SDL_BlitSurface(renderedImage, &srcRect, tempBackbuffer, &srcRect); + SDL_DestroySurface(renderedImage); + // Then convert the result back to the backbuffer format and write it back + SDL_Surface* newBackBuffer = SDL_ConvertSurface(tempBackbuffer, DDBackBuffer->format); + SDL_DestroySurface(tempBackbuffer); + SDL_BlitSurface(newBackBuffer, &srcRect, DDBackBuffer, &srcRect); + SDL_DestroySurface(newBackBuffer); + return DD_OK; + } HRESULT ForceUpdate(int x, int y, int w, int h) override { return DD_OK; } HRESULT Clear() override { return DD_OK; } HRESULT SetCamera(IDirect3DRMFrame* camera) override @@ -449,7 +766,7 @@ struct Direct3DRMImpl : virtual public IDirect3DRM2 { HRESULT CreateMesh(IDirect3DRMMesh** outMesh) override { *outMesh = static_cast(new Direct3DRMMeshImpl); - return DDERR_GENERIC; + return DD_OK; } HRESULT CreateMaterial(D3DVAL power, IDirect3DRMMaterial** outMaterial) override { diff --git a/miniwin/miniwin/src/miniwin_ddraw.cpp b/miniwin/miniwin/src/miniwin_ddraw.cpp index ad23266f..05a94d13 100644 --- a/miniwin/miniwin/src/miniwin_ddraw.cpp +++ b/miniwin/miniwin/src/miniwin_ddraw.cpp @@ -12,6 +12,7 @@ #include SDL_Window* DDWindow; +SDL_Surface* DDBackBuffer; HRESULT DirectDrawImpl::QueryInterface(const GUID& riid, void** ppvObject) { @@ -89,10 +90,14 @@ HRESULT DirectDrawImpl::CreateSurface( if ((lpDDSurfaceDesc->dwFlags & DDSD_BACKBUFFERCOUNT) == DDSD_BACKBUFFERCOUNT) { SDL_Log("Todo: Switch to %d buffering", lpDDSurfaceDesc->dwBackBufferCount); } + SDL_Surface* windowSurface = SDL_GetWindowSurface(DDWindow); + if (!windowSurface) { + return DDERR_GENERIC; + } int width, height; SDL_GetWindowSize(DDWindow, &width, &height); bool implicitFlip = (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_FLIP) != DDSCAPS_FLIP; - auto frontBuffer = new DirectDrawSurfaceImpl(width, height, format); + auto frontBuffer = new DirectDrawSurfaceImpl(width, height, windowSurface->format); frontBuffer->SetAutoFlip(implicitFlip); *lplpDDSurface = static_cast(frontBuffer); return DD_OK; diff --git a/miniwin/miniwin/src/miniwin_ddsurface.cpp b/miniwin/miniwin/src/miniwin_ddsurface.cpp index c0cb5f5f..b04c2e77 100644 --- a/miniwin/miniwin/src/miniwin_ddsurface.cpp +++ b/miniwin/miniwin/src/miniwin_ddsurface.cpp @@ -62,6 +62,10 @@ HRESULT DirectDrawSurfaceImpl::Blt( if (!srcSurface || !srcSurface->m_surface) { return DDERR_GENERIC; } + if (m_autoFlip) { + DDBackBuffer = srcSurface->m_surface; + return Flip(nullptr, DDFLIP_WAIT); + } SDL_Rect srcRect; if (lpSrcRect) { @@ -120,15 +124,15 @@ HRESULT DirectDrawSurfaceImpl::BltFast( HRESULT DirectDrawSurfaceImpl::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DDFlipFlags dwFlags) { - if (!m_surface) { + if (!DDBackBuffer) { return DDERR_GENERIC; } SDL_Surface* windowSurface = SDL_GetWindowSurface(DDWindow); if (!windowSurface) { return DDERR_GENERIC; } - SDL_Rect srcRect{0, 0, m_surface->w, m_surface->h}; - SDL_Surface* copy = SDL_ConvertSurface(m_surface, windowSurface->format); + SDL_Rect srcRect{0, 0, DDBackBuffer->w, DDBackBuffer->h}; + SDL_Surface* copy = SDL_ConvertSurface(DDBackBuffer, windowSurface->format); SDL_BlitSurface(copy, &srcRect, windowSurface, &srcRect); SDL_DestroySurface(copy); SDL_UpdateWindowSurface(DDWindow); @@ -140,6 +144,7 @@ HRESULT DirectDrawSurfaceImpl::GetAttachedSurface(LPDDSCAPS lpDDSCaps, LPDIRECTD if ((lpDDSCaps->dwCaps & DDSCAPS_BACKBUFFER) != DDSCAPS_BACKBUFFER) { return DDERR_INVALIDPARAMS; } + DDBackBuffer = m_surface; *lplpDDAttachedSurface = static_cast(this); return DD_OK; }