diff --git a/README.md b/README.md index 2efec77..68cab72 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Many of the default settings of D2DX can be changed. To use a configuration file Check [DEVELOPMENT.md](https://github.com/jackrayner/d2dx/blob/main/DEVELOPMENT.md) to see whether it is a known or planned item, then [open an issue](https://github.com/jackrayner/d2dx/issues) and include `d2dx_log.txt` from your Diablo II folder, your game version and your OS. ## Credits -D2DX was written by bolrog, with patches from Xenthalon. This fork is maintained by Jack Rayner. +D2DX was written by bolrog, with patches from Xenthalon and Jason Newcomb (Jarcho). This fork is maintained by Jack Rayner. D2DX embeds and uses the following third-party code: - SGD2FreeRes (the resolution mod) by Mir Drualga, licensed under Affero GPL v3. diff --git a/d2dx-defaults.cfg b/d2dx-defaults.cfg index 3ef456e..c4b9674 100644 --- a/d2dx-defaults.cfg +++ b/d2dx-defaults.cfg @@ -30,3 +30,4 @@ noaa=false # if true, will not apply anti-aliasing to jagged edges nocompatmodefix=false # if true, will not block the use of "Windows XP compatibility mode" notitlechange=false # if true, will not change the window title text nomotionprediction=false # if true, will not run the game graphics at high fps +nokeepaspectratio=false # if true, will not keep the aspect ratio when drawing to the screen diff --git a/docs/configuration.md b/docs/configuration.md index 3a9770d..626c3d1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -36,6 +36,7 @@ All default to `false`. Setting one to `true` turns the matching D2DX behaviour | `nocompatmodefix` | Don't block Windows XP compatibility mode. Needed under Wine/Proton: Wine reports Windows 6.2 to one API and 10.0 to another, which D2DX would otherwise take for compatibility mode. (This is from a report of a 1.14d run under Proton, not from reading the Wine source.) | | `notitlechange` | Don't change the window title. | | `nomotionprediction` | Don't run the game graphics at high FPS. | +| `nokeepaspectratio` | Don't keep the aspect ratio when scaling the game to the screen: stretch it to fill the whole window or screen. By default the aspect ratio is kept, with black bars where needed, and cutscenes are scaled to the full screen width. | ### `[debug]` | Key | Default | Meaning | @@ -48,7 +49,7 @@ Options are matched anywhere in the command line. Besides the game's own `-3dfx` | Option | Same as | |---|---| -| `-dxnoclipcursor`, `-dxnofpsfix`, `-dxnoresmod`, `-dxnowide`, `-dxnologo`, `-dxnovsync`, `-dxnoaa`, `-dxnocompatmodefix`, `-dxnotitlechange` | the `[optouts]` key of the same name, set to `true` | +| `-dxnoclipcursor`, `-dxnofpsfix`, `-dxnoresmod`, `-dxnowide`, `-dxnologo`, `-dxnovsync`, `-dxnoaa`, `-dxnocompatmodefix`, `-dxnotitlechange`, `-dxnokeepaspectratio` | the `[optouts]` key of the same name, set to `true` | | `-dxnomop` | `nomotionprediction = true` | | `-dxscale2`, `-dxscale3` | `[window] scale = 2` or `3` | | `-dxdbg_dump_textures` | `[debug] dumptextures = true` | diff --git a/src/d2dx/D2DXContext.cpp b/src/d2dx/D2DXContext.cpp index bd31663..7664dba 100644 --- a/src/d2dx/D2DXContext.cpp +++ b/src/d2dx/D2DXContext.cpp @@ -222,7 +222,7 @@ void D2DXContext::OnSstWinOpen( windowSize.width = width; windowSize.height = height; } - _renderContext->SetSizes(gameSize, windowSize * _options.GetWindowScale()); + _renderContext->SetSizes(gameSize, windowSize * _options.GetWindowScale(), _renderContext->GetScreenMode()); } _batchCount = 0; @@ -323,9 +323,10 @@ void D2DXContext::CheckMajorGameState() { const int32_t batchCount = (int32_t)_batchCount; - if (_majorGameState == MajorGameState::Unknown && batchCount == 0) + if ((_majorGameState == MajorGameState::Unknown || _majorGameState == MajorGameState::FmvIntro) && batchCount == 0) { _majorGameState = MajorGameState::FmvIntro; + return; } _majorGameState = MajorGameState::Menus; @@ -1020,7 +1021,8 @@ void D2DXContext::OnLfbUnlock( const uint32_t* lfbPtr, uint32_t strideInBytes) { - _renderContext->WriteToScreen(lfbPtr, 640, 480); + bool forCinematic = !(_majorGameState == MajorGameState::Unknown || _majorGameState == MajorGameState::FmvIntro); + _renderContext->WriteToScreen(lfbPtr, 640, 480, forCinematic); } _Use_decl_annotations_ diff --git a/src/d2dx/IRenderContext.h b/src/d2dx/IRenderContext.h index 515ce1e..bf3feb6 100644 --- a/src/d2dx/IRenderContext.h +++ b/src/d2dx/IRenderContext.h @@ -55,7 +55,8 @@ namespace d2dx virtual void WriteToScreen( _In_reads_(width * height) const uint32_t* pixels, _In_ int32_t width, - _In_ int32_t height) = 0; + _In_ int32_t height, + _In_ bool forCinematic) = 0; virtual void SetPalette( _In_ int32_t paletteIndex, @@ -68,7 +69,8 @@ namespace d2dx virtual void SetSizes( _In_ Size gameSize, - _In_ Size windowSize) = 0; + _In_ Size windowSize, + _In_ ScreenMode screenMode) = 0; virtual void GetCurrentMetrics( _Out_opt_ Size* gameSize, diff --git a/src/d2dx/Metrics.cpp b/src/d2dx/Metrics.cpp index 042a364..cc7c7f7 100644 --- a/src/d2dx/Metrics.cpp +++ b/src/d2dx/Metrics.cpp @@ -184,8 +184,12 @@ _Use_decl_annotations_ Rect d2dx::Metrics::GetRenderRect( Size gameSize, Size desktopSize, - bool wide) noexcept + bool keepAspect) noexcept { + if (!keepAspect) { + return Rect{ 0, 0, desktopSize.width, desktopSize.height }; + } + int32_t scaleFactor = 1; while ( diff --git a/src/d2dx/Metrics.h b/src/d2dx/Metrics.h index b30de5b..ce87475 100644 --- a/src/d2dx/Metrics.h +++ b/src/d2dx/Metrics.h @@ -14,7 +14,7 @@ namespace d2dx Rect GetRenderRect( _In_ Size gameSize, _In_ Size desktopSize, - _In_ bool wide) noexcept; + _In_ bool keepAspect) noexcept; Buffer GetStandardDesktopSizes() noexcept; } diff --git a/src/d2dx/Options.cpp b/src/d2dx/Options.cpp index 26adff1..cdcc7cc 100644 --- a/src/d2dx/Options.cpp +++ b/src/d2dx/Options.cpp @@ -79,6 +79,7 @@ void Options::ApplyCfg( READ_OPTOUTS_FLAG(OptionsFlag::NoCompatModeFix, "nocompatmodefix"); READ_OPTOUTS_FLAG(OptionsFlag::NoTitleChange, "notitlechange"); READ_OPTOUTS_FLAG(OptionsFlag::NoMotionPrediction, "nomotionprediction"); + READ_OPTOUTS_FLAG(OptionsFlag::NoKeepAspectRatio, "nokeepaspectratio"); #undef READ_OPTOUTS_FLAG } @@ -163,6 +164,7 @@ void Options::ApplyCommandLine( if (strstr(cmdLine, "-dxnocompatmodefix")) SetFlag(OptionsFlag::NoCompatModeFix, true); if (strstr(cmdLine, "-dxnotitlechange")) SetFlag(OptionsFlag::NoTitleChange, true); if (strstr(cmdLine, "-dxnomop")) SetFlag(OptionsFlag::NoMotionPrediction, true); + if (strstr(cmdLine, "-dxnokeepaspectratio")) SetFlag(OptionsFlag::NoKeepAspectRatio, true); if (strstr(cmdLine, "-dxscale3")) SetWindowScale(3); else if (strstr(cmdLine, "-dxscale2")) SetWindowScale(2); diff --git a/src/d2dx/Options.h b/src/d2dx/Options.h index 9521663..de615dd 100644 --- a/src/d2dx/Options.h +++ b/src/d2dx/Options.h @@ -34,6 +34,7 @@ namespace d2dx NoTitleChange, NoVSync, NoMotionPrediction, + NoKeepAspectRatio, DbgDumpTextures, diff --git a/src/d2dx/RenderContext.cpp b/src/d2dx/RenderContext.cpp index cef01d0..e27f1a8 100644 --- a/src/d2dx/RenderContext.cpp +++ b/src/d2dx/RenderContext.cpp @@ -75,7 +75,7 @@ RenderContext::RenderContext( _renderRect = Metrics::GetRenderRect( gameSize, _screenMode == ScreenMode::FullscreenDefault ? _desktopSize : _windowSize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoKeepAspectRatio)); #ifndef NDEBUG ShowCursor_Real(TRUE); @@ -262,18 +262,15 @@ RenderContext::RenderContext( float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; _deviceContext->ClearRenderTargetView(_backbufferRtv.Get(), color); - Size renderTargetSize = _d2dxContext->GetSuggestedCustomResolution(); - renderTargetSize.width = max(1024, renderTargetSize.width); - renderTargetSize.height = max(768, renderTargetSize.height); - _vbCapacity = 1024 * 1024; - SetSizes(_gameSize, _windowSize); + _gameSize = { 0, 0 }; + SetSizes(_gameSize, _windowSize, _screenMode); _resources = std::make_unique( _vbCapacity * sizeof(Vertex), 16 * sizeof(Constants), - renderTargetSize, + gameSize, _device.Get(), simd); @@ -514,24 +511,39 @@ _Use_decl_annotations_ void RenderContext::WriteToScreen( const uint32_t* pixels, int32_t width, - int32_t height) + int32_t height, + bool forCinematic) { D3D11_MAPPED_SUBRESOURCE ms; - D2DX_CHECK_HR(_deviceContext->Map(_resources->GetVideoTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)); - memcpy(ms.pData, pixels, width * height * 4); - _deviceContext->Unmap(_resources->GetVideoTexture(), 0); - SetBlendState(AlphaBlend::Opaque); + uint32_t startVertexLocation = _vbWriteIndex; + uint32_t vertexCount = 0; - SetShaderState( - _resources->GetVertexShader(RenderContextVertexShader::Display), - _resources->GetPixelShader(RenderContextPixelShader::Video), - _resources->GetVideoSrv(), - nullptr); + if (forCinematic) { + SetSizes({ width, 292 }, _windowSize, _screenMode); + D2DX_CHECK_HR(_deviceContext->Map(_resources->GetCinematicTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)); + memcpy(ms.pData, &pixels[width * 94], width * 292 * 4); + _deviceContext->Unmap(_resources->GetCinematicTexture(), 0); + SetShaderState( + _resources->GetVertexShader(RenderContextVertexShader::Display), + _resources->GetPixelShader(RenderContextPixelShader::Video), + _resources->GetCinematicSrv(), + nullptr); + vertexCount = UpdateVerticesWithFullScreenTriangle(_gameSize, _resources->GetCinematicTextureSize(), { 0,0,_gameSize.width, _gameSize.height }); + } + else { + D2DX_CHECK_HR(_deviceContext->Map(_resources->GetVideoTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)); + memcpy(ms.pData, pixels, width * height * 4); + _deviceContext->Unmap(_resources->GetVideoTexture(), 0); + SetShaderState( + _resources->GetVertexShader(RenderContextVertexShader::Display), + _resources->GetPixelShader(RenderContextPixelShader::Video), + _resources->GetVideoSrv(), + nullptr); + vertexCount = UpdateVerticesWithFullScreenTriangle(_gameSize, _resources->GetVideoTextureSize(), { 0,0,_gameSize.width, _gameSize.height }); + UpdateViewport({ 0,0,_gameSize.width, _gameSize.height }); + } - uint32_t startVertexLocation = _vbWriteIndex; - uint32_t vertexCount = UpdateVerticesWithFullScreenTriangle(_gameSize, _resources->GetVideoTextureSize(), { 0,0,_gameSize.width, _gameSize.height }); - UpdateViewport({ 0,0,_gameSize.width, _gameSize.height }); _deviceContext->Draw(vertexCount, startVertexLocation); Present(); @@ -839,17 +851,30 @@ void RenderContext::ResizeBackbuffer() _Use_decl_annotations_ void RenderContext::SetSizes( Size gameSize, - Size windowSize) + Size windowSize, + ScreenMode screenMode) { + if (_gameSize == gameSize && _windowSize == windowSize && _screenMode == screenMode) { + return; + } + + if (_resources && gameSize != _gameSize) { + _resources->SetFramebufferSize(gameSize, _device.Get()); + SetRenderTargets( + _resources->GetFramebufferRtv(RenderContextFramebuffer::Game), + _resources->GetFramebufferRtv(RenderContextFramebuffer::SurfaceId)); + } + _gameSize = gameSize; _windowSize = windowSize; + _screenMode = screenMode; auto displaySize = _screenMode == ScreenMode::FullscreenDefault ? _desktopSize : _windowSize; _renderRect = Metrics::GetRenderRect( _gameSize, displaySize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoKeepAspectRatio)); bool centerOnCurrentPosition = _hasAdjustedWindowPlacement; _hasAdjustedWindowPlacement = true; @@ -892,7 +917,7 @@ void RenderContext::SetSizes( _renderRect = Metrics::GetRenderRect( _gameSize, _windowSize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoKeepAspectRatio)); windowRect = { 0, 0, _windowSize.width, _windowSize.height }; AdjustWindowRect(&windowRect, windowStyle, FALSE); @@ -987,13 +1012,11 @@ void RenderContext::ToggleFullscreen() { if (_screenMode == ScreenMode::FullscreenDefault) { - _screenMode = ScreenMode::Windowed; - SetSizes(_gameSize, _windowSize); + SetSizes(_gameSize, _windowSize, ScreenMode::Windowed); } else { - _screenMode = ScreenMode::FullscreenDefault; - SetSizes(_gameSize, _windowSize); + SetSizes(_gameSize, _windowSize, ScreenMode::FullscreenDefault); } } diff --git a/src/d2dx/RenderContext.h b/src/d2dx/RenderContext.h index d7cabc0..2480f7d 100644 --- a/src/d2dx/RenderContext.h +++ b/src/d2dx/RenderContext.h @@ -86,7 +86,8 @@ namespace d2dx virtual void WriteToScreen( _In_reads_(width* height) const uint32_t* pixels, _In_ int32_t width, - _In_ int32_t height) override; + _In_ int32_t height, + _In_ bool forCinematic) override; virtual void SetPalette( _In_ int32_t paletteIndex, @@ -99,7 +100,8 @@ namespace d2dx virtual void SetSizes( _In_ Size gameSize, - _In_ Size windowSize) override; + _In_ Size windowSize, + _In_ ScreenMode screenMode) override; virtual void GetCurrentMetrics( _Out_opt_ Size* gameSize, diff --git a/src/d2dx/RenderContextResources.cpp b/src/d2dx/RenderContextResources.cpp index 004b4ac..6b4cfdb 100644 --- a/src/d2dx/RenderContextResources.cpp +++ b/src/d2dx/RenderContextResources.cpp @@ -81,6 +81,22 @@ ITextureCache* RenderContextResources::GetTextureCache( return _textureCaches[log2Longest].get(); } +void RenderContextResources::SetFramebufferSize( + Size framebufferSize, + ID3D11Device* device) +{ + _framebuffers[0].texture = nullptr; + _framebuffers[0].rtv= nullptr; + _framebuffers[0].srv= nullptr; + _framebuffers[1].texture = nullptr; + _framebuffers[1].rtv = nullptr; + _framebuffers[1].srv = nullptr; + _framebuffers[2].texture = nullptr; + _framebuffers[2].rtv = nullptr; + _framebuffers[2].srv = nullptr; + CreateFramebuffers(framebufferSize, device); +} + _Use_decl_annotations_ void RenderContextResources::CreateShadersAndInputLayout( ID3D11Device* device) @@ -254,12 +270,22 @@ void RenderContextResources::CreateVideoTextures( }; _videoTextureSize = { 640, 480 }; + _cinematicTextureSize = { 640, 292 }; D2DX_CHECK_HR( device->CreateTexture2D(&desc, NULL, &_videoTexture)); D2DX_CHECK_HR( device->CreateShaderResourceView(_videoTexture.Get(), NULL, &_videoTextureSrv)); + + desc.Height = 292; + + D2DX_CHECK_HR( + device->CreateTexture2D(&desc, NULL, &_cinematicTexture)); + + D2DX_CHECK_HR( + device->CreateShaderResourceView(_cinematicTexture.Get(), NULL, &_cinematicTextureSrv)); + } _Use_decl_annotations_ diff --git a/src/d2dx/RenderContextResources.h b/src/d2dx/RenderContextResources.h index 2117848..a16da2e 100644 --- a/src/d2dx/RenderContextResources.h +++ b/src/d2dx/RenderContextResources.h @@ -79,6 +79,8 @@ namespace d2dx void OnNewFrame(); + void SetFramebufferSize(Size framebufferSize, ID3D11Device* device); + ID3D11InputLayout* GetInputLayout() const { return _inputLayout.Get(); } ID3D11VertexShader* GetVertexShader(RenderContextVertexShader vertexShader) const @@ -120,6 +122,21 @@ namespace d2dx return _videoTextureSrv.Get(); } + Size GetCinematicTextureSize() const + { + return _cinematicTextureSize; + } + + ID3D11Texture2D* GetCinematicTexture() const + { + return _cinematicTexture.Get(); + } + + ID3D11ShaderResourceView* GetCinematicSrv() const + { + return _cinematicTextureSrv.Get(); + } + ID3D11RasterizerState* GetRasterizerState(bool enableScissor) const { return enableScissor ? _rasterizerState.Get() : _rasterizerStateNoScissor.Get(); @@ -220,6 +237,10 @@ namespace d2dx ComPtr _videoTexture; ComPtr _videoTextureSrv; + Size _cinematicTextureSize; + ComPtr _cinematicTexture; + ComPtr _cinematicTextureSrv; + std::unique_ptr _textureCaches[7]; ComPtr _rasterizerStateNoScissor; diff --git a/src/d2dxtests/TestMetrics.cpp b/src/d2dxtests/TestMetrics.cpp index 35804b5..e58e4da 100644 --- a/src/d2dxtests/TestMetrics.cpp +++ b/src/d2dxtests/TestMetrics.cpp @@ -62,10 +62,10 @@ namespace d2dxtests Assert::AreEqual(Size(1436, 720), suggestedGameSize); } - void AssertThatGameSizeIsIntegerScale(Size desktopSize, bool wide, bool lenient) + void AssertThatGameSizeIsIntegerScale(Size desktopSize, bool wide, bool lenient, bool keepAspect = true) { auto suggestedGameSize = d2dx::Metrics::GetSuggestedGameSize(desktopSize, wide); - auto renderRect = d2dx::Metrics::GetRenderRect(suggestedGameSize, desktopSize, wide); + auto renderRect = d2dx::Metrics::GetRenderRect(suggestedGameSize, desktopSize, keepAspect); Assert::IsTrue(renderRect.offset.x >= 0); Assert::IsTrue(renderRect.offset.y >= 0); Assert::IsTrue(renderRect.size.width > 0); @@ -73,6 +73,16 @@ namespace d2dxtests Assert::IsTrue((renderRect.offset.x + renderRect.size.width) <= desktopSize.width); Assert::IsTrue((renderRect.offset.y + renderRect.size.height) <= desktopSize.height); + if (!keepAspect) + { + /* Not keeping the aspect ratio means stretching to the whole desktop. */ + Assert::AreEqual(0, renderRect.offset.x); + Assert::AreEqual(0, renderRect.offset.y); + Assert::AreEqual(desktopSize.width, renderRect.size.width); + Assert::AreEqual(desktopSize.height, renderRect.size.height); + return; + } + if (renderRect.offset.x > 0 && renderRect.offset.y > 0) { Assert::IsTrue(renderRect.offset.x < 16 || renderRect.offset.y < 16); @@ -103,6 +113,8 @@ namespace d2dxtests { AssertThatGameSizeIsIntegerScale(standardDesktopSizes.items[i], false, false); AssertThatGameSizeIsIntegerScale(standardDesktopSizes.items[i], true, false); + AssertThatGameSizeIsIntegerScale(standardDesktopSizes.items[i], false, false, false); + AssertThatGameSizeIsIntegerScale(standardDesktopSizes.items[i], true, false, false); } } @@ -112,11 +124,15 @@ namespace d2dxtests { AssertThatGameSizeIsIntegerScale({ width, 503 }, false, true); AssertThatGameSizeIsIntegerScale({ width, 503 }, true, true); + AssertThatGameSizeIsIntegerScale({ width, 503 }, false, true, false); + AssertThatGameSizeIsIntegerScale({ width, 503 }, true, true, false); } for (int32_t height = 50; height < 1600; ++height) { AssertThatGameSizeIsIntegerScale({ 614, height }, false, true); AssertThatGameSizeIsIntegerScale({ 614, height }, true, true); + AssertThatGameSizeIsIntegerScale({ 614, height }, false, true, false); + AssertThatGameSizeIsIntegerScale({ 614, height }, true, true, false); } } };