mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Normalize swapchain backbuffers for the opt-in D3D9 single-sample path
This commit is contained in:
1 parent
8d9b7084f8
commit
0fd3a1a2b8
5 files changed
+363
No files matched your search
@@ -29,6 +29,19 @@ alpha-to-coverage. It is a compatibility override, not transparent MSAA emulatio
|
||||
or a universal PowerVR default. Existing low32, BC-format, present synchronization
|
||||
and tiler workarounds must remain intact.
|
||||
|
||||
Swapchain backbuffers have a separate creation path. With the override enabled,
|
||||
that path now validates a copy of the original requested descriptor and transfers
|
||||
the effective sample type/quality before allocation, so automatic depth surfaces
|
||||
and backbuffers agree. Validation uses the application's lockable-backbuffer flag;
|
||||
the internal lockability required for GDI fallback is retained on the allocated
|
||||
backbuffer. With the override disabled the original backbuffer path is unchanged.
|
||||
This correction addresses official PR602 review 5486295088. Its CPU harness
|
||||
compiles the actual DXVK sample decoder, surface normalizer and backbuffer method,
|
||||
plus the automatic-depth descriptor and framebuffer depth-binding excerpts.
|
||||
Fifteen cases cover on/off, NONMASKABLE quality, matching depth, no-depth operation,
|
||||
unsupported/invalid/lockable requests, allocation cleanup and recreation. Backend
|
||||
images and adapter capabilities are mocked; these are not GPU or gameplay tests.
|
||||
|
||||
## Independent HUD correction
|
||||
|
||||
The diagnostic frametimes HUD bound an R8_UINT storage texel buffer to a shader
|
||||
@@ -172,6 +185,9 @@ runtime or APK. Its output directory must be new or empty (including hidden
|
||||
entries). Prior or partially built outputs are refused before fetching sources,
|
||||
left untouched and never included in a new DLL manifest. Use a fresh directory
|
||||
after a failed build.
|
||||
The recipe also requires a native C++17 compiler for
|
||||
`python3 tools/dxvk/check_d3d9_single_sample.py /path/to/patched-dxvk` before DLL
|
||||
compilation. Add `--sanitize` on supported hosts to run its ASan/UBSan controls.
|
||||
|
||||
The compositor ownership safeguards are independently proposed in
|
||||
<https://github.com/Droid-Deck/DroidDeck/pull/561>. Those native changes were present
|
||||
|
||||
@@ -27,6 +27,7 @@ for patch in "$root"/tools/dxvk/patches/*.patch; do
|
||||
done
|
||||
python3 "$root/tools/dxvk/check_present_sync.py" "$work/source/src/dxvk/dxvk_presenter.cpp"
|
||||
python3 "$root/tools/dxvk/check_hud_storage_format.py" "$work/source/src/dxvk/hud/shaders/hud_frame_time_eval.comp"
|
||||
python3 "$root/tools/dxvk/check_d3d9_single_sample.py" "$work/source"
|
||||
for arch in 32 64; do
|
||||
meson setup "$work/build$arch" "$work/source" --cross-file "$work/source/build-win$arch.txt" \
|
||||
--buildtype release --prefix "$out" --bindir "x$arch" --libdir "x$arch" -Db_ndebug=if-release --strip
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compile pinned DXVK's actual descriptor paths against a controlled backend.
|
||||
|
||||
This checks CPU control flow, not GPU rendering. No Wine/game/device is started.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
CASES = ('forced_msaa_depth', 'default_msaa_depth', 'forced_nonmaskable',
|
||||
'default_nonmaskable', 'forced_already_single', 'forced_no_depth',
|
||||
'forced_unsupported', 'forced_invalid_type', 'forced_lockable_msaa',
|
||||
'forced_lockable_single', 'forced_allocation_failure',
|
||||
'default_allocation_failure', 'forced_recreate', 'ordinary_surface',
|
||||
'ordinary_invalid_lockable')
|
||||
|
||||
|
||||
def function(text, signature):
|
||||
start = text.index(signature)
|
||||
opening = text.index('{', start)
|
||||
# Ignore braces in comments and strings while preserving source positions.
|
||||
stripped = re.sub(r'//[^\n]*|/\*.*?\*/|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'',
|
||||
lambda m: ' ' * len(m.group()), text, flags=re.S)
|
||||
depth = 0
|
||||
for index in range(opening, len(stripped)):
|
||||
depth += (stripped[index] == '{') - (stripped[index] == '}')
|
||||
if depth == 0:
|
||||
return text[start:index + 1]
|
||||
raise ValueError('Unterminated source function: ' + signature)
|
||||
|
||||
|
||||
def production(source):
|
||||
base = source / 'src/d3d9'
|
||||
common = (base / 'd3d9_common_texture.cpp').read_text()
|
||||
header = (base / 'd3d9_common_texture.h').read_text()
|
||||
swap = (base / 'd3d9_swapchain.cpp').read_text()
|
||||
device = (base / 'd3d9_device.cpp').read_text()
|
||||
util = (base / 'd3d9_util.cpp').read_text()
|
||||
pieces = {
|
||||
'decode': function(util, 'HRESULT DecodeMultiSampleType('),
|
||||
'normalize': function(common, 'HRESULT D3D9CommonTexture::NormalizeTextureProperties('),
|
||||
'backbuffers': function(swap, 'HRESULT D3D9SwapChainEx::CreateBackBuffers('),
|
||||
'uses_image': function(header, 'static bool TextureUsesImage('),
|
||||
}
|
||||
reset = function(device, 'HRESULT D3D9DeviceEx::ResetSwapChain(')
|
||||
begin = reset.index('D3D9_COMMON_TEXTURE_DESC desc;', reset.index('if (pPresentationParameters->EnableAutoDepthStencil)'))
|
||||
end = reset.index('m_autoDepthStencil =', begin)
|
||||
pieces['auto_depth'] = reset[begin:end].rstrip()
|
||||
framebuffer = function(device, 'void D3D9DeviceEx::BindFramebuffer(')
|
||||
begin = framebuffer.index('const bool mismatch =')
|
||||
end = framebuffer.index(';', framebuffer.index('const bool bindDS =', begin)) + 1
|
||||
pieces['depth_binding'] = framebuffer[begin:end]
|
||||
return pieces
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('source', type=Path, help='Pinned DXVK tree with downstream patches applied')
|
||||
parser.add_argument('--cxx', default=os.environ.get('CXX', 'c++'))
|
||||
parser.add_argument('--sanitize', action='store_true', help='Enable ASan/UBSan on the CPU harness')
|
||||
args = parser.parse_args()
|
||||
pieces = production(args.source)
|
||||
harness = (Path(__file__).parent / 'tests/d3d9_single_sample.cpp').read_text()
|
||||
harness = harness.replace('/* PRODUCTION_USES_IMAGE */', pieces['uses_image'])
|
||||
with tempfile.TemporaryDirectory(prefix='dxvk-d3d9-') as directory:
|
||||
out = Path(directory)
|
||||
(out / 'harness.cpp').write_text(harness)
|
||||
include = '\n\n'.join(pieces[key] for key in ('decode', 'normalize', 'backbuffers'))
|
||||
include += '\nHRESULT auto_depth(D3D9DeviceEx* device, D3DPRESENT_PARAMETERS* pPresentationParameters, D3D9_COMMON_TEXTURE_DESC* output) {\n'
|
||||
include += 'auto* self = device;\n' + pieces['auto_depth'].replace('(this,', '(self,')
|
||||
include += '\n*output = desc; return D3D_OK;\n}\n'
|
||||
include += '\nbool depth_binds(const ImageInfo& color, const ImageInfo& dsInfo) {\n'
|
||||
include += 'auto sampleCount = color.sampleCount; auto renderArea = color.extent;\n'
|
||||
include += 'const bool noRT0Bound = false, ignoreRT0 = false;\n'
|
||||
include += pieces['depth_binding'] + '\nreturn bindDS;\n}\n'
|
||||
(out / 'd3d9_production.inc').write_text(include)
|
||||
command = [args.cxx, '-std=c++17', '-Wall', '-Wextra', '-Werror',
|
||||
'-Wno-unused-parameter', str(out / 'harness.cpp'), '-o', str(out / 'check')]
|
||||
# The original CreateBackBuffers does not use Flags; that known baseline
|
||||
# warning must not turn a negative control into a compiler-fixture error.
|
||||
if args.sanitize:
|
||||
command[1:1] = ['-fsanitize=address,undefined', '-fno-omit-frame-pointer']
|
||||
compilation = subprocess.run(command, capture_output=True, text=True)
|
||||
if compilation.returncode:
|
||||
raise SystemExit(compilation.stderr)
|
||||
results = []
|
||||
for case in CASES:
|
||||
process = subprocess.run([str(out / 'check'), case], capture_output=True,
|
||||
text=True, timeout=10, cwd=out,
|
||||
env={'ASAN_OPTIONS': 'detect_leaks=' +
|
||||
('0' if sys.platform == 'darwin' else '1')})
|
||||
results.append({'case': case, 'passed': process.returncode == 0,
|
||||
'exit': process.returncode, 'detail': process.stderr.strip()[-800:]})
|
||||
result = {'cases': results, 'passed': sum(row['passed'] for row in results),
|
||||
'failed': sum(not row['passed'] for row in results),
|
||||
'production_sha256': {key: hashlib.sha256(value.encode()).hexdigest()
|
||||
for key, value in pieces.items()},
|
||||
'backend_and_adapter_mocked': True, 'GPU_test': False,
|
||||
'sanitizers': args.sanitize,
|
||||
'leak_detection': args.sanitize and sys.platform != 'darwin'}
|
||||
print(json.dumps(result, indent=2))
|
||||
return bool(result['failed'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
@@ -64,3 +64,23 @@ index e69a39a..4478420 100644
|
||||
/// Allow D3DLOCK_DISCARD
|
||||
bool allowDiscard;
|
||||
|
||||
diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp
|
||||
--- a/src/d3d9/d3d9_swapchain.cpp
|
||||
+++ b/src/d3d9/d3d9_swapchain.cpp
|
||||
@@ -1055,1 +1055,16 @@ namespace dxvk {
|
||||
+ if (m_parent->GetOptions()->forceSingleSample) {
|
||||
+ // Backbuffers bypass the normal surface-creation entry points. Validate
|
||||
+ // the application's request, then apply the same effective sample count.
|
||||
+ // IsLockable above is an internal GDI fallback requirement, not the
|
||||
+ // application's lockability request used by MSAA validation.
|
||||
+ auto requestedDesc = desc;
|
||||
+ requestedDesc.IsLockable = (Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER) != 0;
|
||||
+ HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(
|
||||
+ m_parent, D3DRTYPE_SURFACE, &requestedDesc);
|
||||
+ if (FAILED(hr))
|
||||
+ return hr;
|
||||
+ desc.MultiSample = requestedDesc.MultiSample;
|
||||
+ desc.MultisampleQuality = requestedDesc.MultisampleQuality;
|
||||
+ }
|
||||
+
|
||||
const bool isExtended = m_parent->IsD3DCompatibile(D3DCompatibility::D3D9Ex);
|
||||
@@ -0,0 +1,214 @@
|
||||
// Backend/adapter fixture for the verbatim DXVK functions in d3d9_production.inc.
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using HRESULT = int32_t;
|
||||
using DWORD = uint32_t;
|
||||
using UINT = uint32_t;
|
||||
using VkSampleCountFlagBits = uint32_t;
|
||||
constexpr HRESULT D3D_OK = 0, D3DERR_INVALIDCALL = -1,
|
||||
D3DERR_NOTAVAILABLE = -2, D3DERR_OUTOFVIDEOMEMORY = -3;
|
||||
constexpr uint32_t VK_SAMPLE_COUNT_1_BIT = 1, VK_IMAGE_ASPECT_COLOR_BIT = 1;
|
||||
constexpr bool FALSE = false, TRUE = true;
|
||||
#define FAILED(x) ((x) < 0)
|
||||
#define unlikely(x) (x)
|
||||
enum D3DMULTISAMPLE_TYPE { D3DMULTISAMPLE_NONE, D3DMULTISAMPLE_NONMASKABLE,
|
||||
D3DMULTISAMPLE_16_SAMPLES = 16 };
|
||||
enum D3DRESOURCETYPE { D3DRTYPE_SURFACE, D3DRTYPE_CUBETEXTURE, D3DRTYPE_VOLUMETEXTURE };
|
||||
enum D3DPOOL { D3DPOOL_DEFAULT, D3DPOOL_MANAGED, D3DPOOL_SYSTEMMEM, D3DPOOL_SCRATCH };
|
||||
enum class D3DCompatibility { D3D9Ex };
|
||||
enum class D3D9Format { X8R8G8B8, D24S8, A8, NULL_FORMAT, ATI2,
|
||||
D32_LOCKABLE, D32F_LOCKABLE, D16_LOCKABLE, S8_LOCKABLE };
|
||||
constexpr DWORD D3DUSAGE_RENDERTARGET = 1, D3DUSAGE_DEPTHSTENCIL = 2,
|
||||
D3DUSAGE_DYNAMIC = 4, D3DUSAGE_WRITEONLY = 8, D3DUSAGE_AUTOGENMIPMAP = 16;
|
||||
constexpr DWORD D3DPRESENTFLAG_LOCKABLE_BACKBUFFER = 1, D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL = 2;
|
||||
constexpr int D3DDEVTYPE_HAL = 1, VK_IMAGE_LAYOUT_UNDEFINED = 0;
|
||||
struct Extent { uint32_t width, height, depth = 1; };
|
||||
namespace util {
|
||||
uint32_t computeMipLevelCount(Extent e) {
|
||||
uint32_t n = std::max({e.width, e.height, e.depth}), levels = 0;
|
||||
do { ++levels; n >>= 1; } while (n);
|
||||
return levels;
|
||||
}
|
||||
}
|
||||
bool IsPoolManaged(D3DPOOL pool) { return pool == D3DPOOL_MANAGED; }
|
||||
bool IsDXTFormat(D3D9Format) { return false; }
|
||||
bool IsDepthStencilFormat(D3D9Format format) { return format == D3D9Format::D24S8; }
|
||||
bool IsLockableDepthStencilFormat(D3D9Format) { return false; }
|
||||
struct D3D9_FORMAT_BLOCK_SIZE { uint32_t Width, Height; };
|
||||
D3D9_FORMAT_BLOCK_SIZE GetFormatAlignedBlockSize(D3D9Format) { return {1, 1}; }
|
||||
D3D9Format EnumerateFormat(D3D9Format format) { return format; }
|
||||
struct D3D9_COMMON_TEXTURE_DESC {
|
||||
uint32_t Width = 1280, Height = 720, Depth = 1, MipLevels = 1, ArraySize = 1;
|
||||
D3D9Format Format = D3D9Format::X8R8G8B8;
|
||||
D3DMULTISAMPLE_TYPE MultiSample = D3DMULTISAMPLE_NONE;
|
||||
DWORD MultisampleQuality = 0;
|
||||
D3DPOOL Pool = D3DPOOL_DEFAULT;
|
||||
DWORD Usage = D3DUSAGE_RENDERTARGET;
|
||||
bool Discard = false, IsBackBuffer = false, IsAttachmentOnly = false, IsLockable = false;
|
||||
};
|
||||
struct D3DPRESENT_PARAMETERS {
|
||||
uint32_t BackBufferWidth = 1280, BackBufferHeight = 720;
|
||||
D3D9Format BackBufferFormat = D3D9Format::X8R8G8B8;
|
||||
D3DMULTISAMPLE_TYPE MultiSampleType = D3DMULTISAMPLE_TYPE(4);
|
||||
DWORD MultiSampleQuality = 0, Flags = 0;
|
||||
D3D9Format AutoDepthStencilFormat = D3D9Format::D24S8;
|
||||
};
|
||||
struct Options {
|
||||
bool forceSingleSample = false, extraFrontbuffer = false,
|
||||
disableA8RT = false, supportCubeDepthFormats = false;
|
||||
};
|
||||
struct FormatMapping {
|
||||
uint32_t Aspect;
|
||||
bool IsValid() const { return true; }
|
||||
};
|
||||
struct Adapter {
|
||||
bool supported = true;
|
||||
std::vector<D3DMULTISAMPLE_TYPE> checked;
|
||||
HRESULT CheckDeviceMultiSampleType(int, D3D9Format, bool, D3DMULTISAMPLE_TYPE samples, void*) {
|
||||
checked.push_back(samples);
|
||||
return supported ? D3D_OK : D3DERR_NOTAVAILABLE;
|
||||
}
|
||||
};
|
||||
struct ImageInfo { Extent extent; VkSampleCountFlagBits sampleCount; };
|
||||
struct DxvkImage { ImageInfo info; };
|
||||
template<typename T> using Rc = std::shared_ptr<T>;
|
||||
template<typename T, unsigned N> using small_vector = std::vector<T>;
|
||||
struct DxvkContext {
|
||||
unsigned initialized = 0;
|
||||
void initImage(const Rc<DxvkImage>&, int) { ++initialized; }
|
||||
};
|
||||
struct D3D9DeviceEx {
|
||||
Options options;
|
||||
Adapter adapter;
|
||||
DxvkContext context;
|
||||
unsigned attempts = 0, failAt = 0, losable = 0, live = 0, injected = 0;
|
||||
std::vector<D3D9_COMMON_TEXTURE_DESC> created;
|
||||
Options* GetOptions() { return &options; }
|
||||
Adapter* GetAdapter() { return &adapter; }
|
||||
FormatMapping LookupFormat(D3D9Format format) { return {IsDepthStencilFormat(format) ? 2u : 1u}; }
|
||||
struct Unsupported { unsigned elementSize = 0; };
|
||||
Unsupported unsupported;
|
||||
const Unsupported* UnsupportedFormatInfo(D3D9Format) { return &unsupported; }
|
||||
bool IsD3DCompatibile(D3DCompatibility) { return false; }
|
||||
void IncrementLosableCounter() { ++losable; }
|
||||
template<typename F> void InjectCs(F function) { ++injected; function(&context); }
|
||||
};
|
||||
HRESULT DecodeMultiSampleType(D3DMULTISAMPLE_TYPE, DWORD, VkSampleCountFlagBits*);
|
||||
struct D3D9CommonTexture {
|
||||
Rc<DxvkImage> image;
|
||||
const Rc<DxvkImage>& GetImage() const { return image; }
|
||||
static HRESULT NormalizeTextureProperties(D3D9DeviceEx*, D3DRESOURCETYPE, D3D9_COMMON_TEXTURE_DESC*);
|
||||
/* PRODUCTION_USES_IMAGE */
|
||||
};
|
||||
class DxvkError : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
std::string message() const { return what(); }
|
||||
};
|
||||
struct Logger { static void err(const std::string&) {} };
|
||||
struct D3D9SwapChainEx;
|
||||
struct D3D9Surface {
|
||||
D3D9CommonTexture texture;
|
||||
D3D9DeviceEx* device;
|
||||
D3D9Surface(D3D9DeviceEx* d, D3D9_COMMON_TEXTURE_DESC* desc, bool, D3D9SwapChainEx*, void*) : device(d) {
|
||||
if (++d->attempts == d->failAt) throw DxvkError("controlled allocation failure");
|
||||
VkSampleCountFlagBits samples;
|
||||
if (FAILED(DecodeMultiSampleType(desc->MultiSample, desc->MultisampleQuality, &samples)))
|
||||
throw DxvkError("invalid backend sample count");
|
||||
texture.image = std::make_shared<DxvkImage>(DxvkImage{{{desc->Width, desc->Height}, samples}});
|
||||
d->created.push_back(*desc); ++d->live;
|
||||
}
|
||||
~D3D9Surface() { --device->live; }
|
||||
D3D9CommonTexture* GetCommonTexture() { return &texture; }
|
||||
};
|
||||
struct D3D9SwapChainEx {
|
||||
D3D9DeviceEx* m_parent;
|
||||
D3DPRESENT_PARAMETERS m_presentParams;
|
||||
std::vector<std::unique_ptr<D3D9Surface>> m_backBuffers;
|
||||
bool flip = false;
|
||||
unsigned destroys = 0;
|
||||
bool SwapWithFrontBuffer() { return flip; }
|
||||
void DestroyBackBuffers() { ++destroys; m_backBuffers.clear(); }
|
||||
HRESULT CreateBackBuffers(uint32_t, DWORD);
|
||||
};
|
||||
#include "d3d9_production.inc"
|
||||
|
||||
void paired(bool enabled, D3DMULTISAMPLE_TYPE type, DWORD quality) {
|
||||
D3D9DeviceEx device; device.options.forceSingleSample = enabled;
|
||||
device.options.extraFrontbuffer = true;
|
||||
D3D9SwapChainEx chain{&device, {}, {}};
|
||||
chain.m_presentParams.MultiSampleType = type; chain.m_presentParams.MultiSampleQuality = quality;
|
||||
assert(chain.CreateBackBuffers(2, 0) == D3D_OK);
|
||||
D3D9_COMMON_TEXTURE_DESC depth;
|
||||
assert(auto_depth(&device, &chain.m_presentParams, &depth) == D3D_OK);
|
||||
VkSampleCountFlagBits requested, dsSamples;
|
||||
assert(DecodeMultiSampleType(type, quality, &requested) == D3D_OK);
|
||||
assert(DecodeMultiSampleType(depth.MultiSample, depth.MultisampleQuality, &dsSamples) == D3D_OK);
|
||||
assert(chain.m_backBuffers.size() == 3 && device.context.initialized == 3);
|
||||
for (const auto& surface : chain.m_backBuffers) {
|
||||
const auto& image = surface->GetCommonTexture()->GetImage()->info;
|
||||
assert(image.sampleCount == (enabled ? 1u : requested));
|
||||
assert(depth_binds(image, {{depth.Width, depth.Height}, dsSamples}));
|
||||
}
|
||||
for (const auto& desc : device.created) {
|
||||
assert(desc.IsLockable && desc.IsBackBuffer && !desc.IsAttachmentOnly);
|
||||
assert(desc.MultiSample == (enabled ? D3DMULTISAMPLE_NONE : type));
|
||||
assert(desc.MultisampleQuality == (enabled ? 0u : quality));
|
||||
}
|
||||
// Original presentation parameters remain available for reset/auto-depth validation.
|
||||
assert(chain.m_presentParams.MultiSampleType == type && chain.m_presentParams.MultiSampleQuality == quality);
|
||||
if (enabled && requested > 1) assert(device.adapter.checked.front() == D3DMULTISAMPLE_TYPE(requested));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
assert(argc == 2); std::string name = argv[1];
|
||||
if (name == "forced_msaa_depth") paired(true, D3DMULTISAMPLE_TYPE(4), 0);
|
||||
else if (name == "default_msaa_depth") paired(false, D3DMULTISAMPLE_TYPE(4), 0);
|
||||
else if (name == "forced_nonmaskable") paired(true, D3DMULTISAMPLE_NONMASKABLE, 2);
|
||||
else if (name == "default_nonmaskable") paired(false, D3DMULTISAMPLE_NONMASKABLE, 2);
|
||||
else if (name == "forced_already_single") paired(true, D3DMULTISAMPLE_NONE, 0);
|
||||
else if (name == "forced_no_depth" || name == "forced_lockable_single") {
|
||||
D3D9DeviceEx d; d.options.forceSingleSample = true;
|
||||
D3D9SwapChainEx c{&d, {}, {}};
|
||||
if (name == "forced_lockable_single") c.m_presentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
|
||||
assert(c.CreateBackBuffers(1, name == "forced_lockable_single" ? D3DPRESENTFLAG_LOCKABLE_BACKBUFFER : 0) == D3D_OK);
|
||||
assert(c.m_backBuffers.size() == 1 && d.created[0].MultiSample == D3DMULTISAMPLE_NONE);
|
||||
assert(d.created[0].IsLockable && d.context.initialized == 1);
|
||||
} else if (name == "forced_unsupported" || name == "forced_invalid_type" || name == "forced_lockable_msaa") {
|
||||
D3D9DeviceEx d; d.options.forceSingleSample = true;
|
||||
D3D9SwapChainEx c{&d, {}, {}};
|
||||
if (name == "forced_unsupported") d.adapter.supported = false;
|
||||
if (name == "forced_invalid_type") c.m_presentParams.MultiSampleType = D3DMULTISAMPLE_TYPE(17);
|
||||
HRESULT expected = name == "forced_unsupported" ? D3DERR_NOTAVAILABLE : D3DERR_INVALIDCALL;
|
||||
assert(c.CreateBackBuffers(2, name == "forced_lockable_msaa" ? D3DPRESENTFLAG_LOCKABLE_BACKBUFFER : 0) == expected);
|
||||
assert(d.attempts == 0 && d.losable == 0 && d.injected == 0 && c.m_backBuffers.empty());
|
||||
} else if (name == "forced_allocation_failure" || name == "default_allocation_failure") {
|
||||
D3D9DeviceEx d; d.options.forceSingleSample = name == "forced_allocation_failure"; d.failAt = 2;
|
||||
D3D9SwapChainEx c{&d, {}, {}};
|
||||
assert(c.CreateBackBuffers(3, 0) == D3DERR_OUTOFVIDEOMEMORY);
|
||||
assert(d.attempts == 2 && d.losable == 1 && d.live == 0 && d.injected == 0 && c.m_backBuffers.empty());
|
||||
} else if (name == "forced_recreate") {
|
||||
D3D9DeviceEx d; d.options.forceSingleSample = true;
|
||||
D3D9SwapChainEx c{&d, {}, {}};
|
||||
assert(c.CreateBackBuffers(3, 0) == D3D_OK);
|
||||
c.m_presentParams.MultiSampleType = D3DMULTISAMPLE_TYPE(8);
|
||||
assert(c.CreateBackBuffers(1, 0) == D3D_OK);
|
||||
assert(d.live == 1 && c.m_backBuffers.size() == 1 && c.destroys == 2);
|
||||
assert(d.adapter.checked.size() == 2 && d.adapter.checked.back() == D3DMULTISAMPLE_TYPE(8));
|
||||
assert(c.m_backBuffers[0]->GetCommonTexture()->GetImage()->info.sampleCount == 1);
|
||||
} else if (name == "ordinary_surface" || name == "ordinary_invalid_lockable") {
|
||||
D3D9DeviceEx d; d.options.forceSingleSample = true;
|
||||
D3D9_COMMON_TEXTURE_DESC desc; desc.MultiSample = D3DMULTISAMPLE_TYPE(4);
|
||||
desc.IsLockable = name == "ordinary_invalid_lockable";
|
||||
assert(D3D9CommonTexture::NormalizeTextureProperties(&d, D3DRTYPE_SURFACE, &desc)
|
||||
== (desc.IsLockable ? D3DERR_INVALIDCALL : D3D_OK));
|
||||
if (!desc.IsLockable) assert(desc.MultiSample == D3DMULTISAMPLE_NONE && desc.MultisampleQuality == 0);
|
||||
} else assert(false);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user