-
Notifications
You must be signed in to change notification settings - Fork 951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
D3D12 cleanup #6200
D3D12 cleanup #6200
Conversation
e736419
to
ef0d8c8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glorious.
ef0d8c8
to
6d9fc2e
Compare
…s instead of using out params
…rror::Unexpected` These are guaranteed to be populated if the creation methods didn't error. If they are not, the driver/runtime is at fault.
Also fixes an issue where only high performance adapters would be enumerated if DXGI 1.6 is available.
…K` & `MULTIPLE_DEVICES` tests The test harness creates the instance and adapter that a test should use. Tests should not create these with default configurations or configs from the environment.
6d9fc2e
to
8429f65
Compare
@@ -149,7 +149,6 @@ gpu-descriptor = "0.3" | |||
bit-set = "0.8" | |||
gpu-allocator = { version = "0.27", default-features = false } | |||
range-alloc = "0.1" | |||
hassle-rs = "0.11.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference, the only reason why hassle-rs
cannot use windows-rs
as bindings is because the windows-rs
maintainers refuse to allow basic Linux support. DirectxShaderConverter works on Linux via the same COM bindings otherwise.
(Either way the abstraction provided by hassle-rs
is far from ideal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it more straightforward to just use the bindings directly since we have to deal with them for D3D12 anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the abstraction by hassle-rs
isn't that useful. It's mostly creating the bindings but we have those now (except not for Linux 😭) in windows-rs
.
.ok() | ||
// TODO: If there's a HRESULT, error may still be non-null and | ||
// contain info. | ||
.into_device_result("Root signature serialization")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to check the error
blob before bailing out with into_device_result()
, missed this during the previous cleanup. The TODO remains valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, is that something we should handle? What would be an example?
The only scenario I can think of is: the blob getting partially filled with errors after which we get an OOM but it's probably ok to error with an OOM instead of reporting the partial errors.
I wish the exact error codes were documented (like in Vulkan), now we have to guess which variants we might run into...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was expecting a similar setup like Compile()
the error doesn't need to be OOM.
For Compile()
, I'm not sure which HRESULT
s to check either. There is:
- The result from
Compile()
; - Non-null
error
blob; - Null
blob
; - Result from
IDxcOperationResult::GetStatus()
.
(and any getters on IDxcOperationResult
/IDxcResult
can fail as well)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a reply in #6200 (comment).
let mut result__ = None; | ||
|
||
(func)(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)) | ||
.ok() | ||
.into_device_result("GetDebugInterface")?; | ||
|
||
let mut result__ = core::ptr::null_mut(); | ||
Ok((func)(&Direct3D12::ID3D12Debug::IID, &mut result__) | ||
.and_then(|| unsafe { windows_core::Type::from_abi(result__) })) | ||
result__.ok_or(crate::DeviceError::Unexpected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that a lot of this was intentionally verbatim-copied from windows-rs
to allow easier updating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, but I think it's more valuable to have those be consistent. I'm also not a fan of using the from_abi
function which maps null to Error::empty()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right yes, that is unfortunate.
Perhaps this also has to do with SAL annotations, if I remember correctly there are different annotations that allow return values to be NULL
, versus them only being NULL
when a non-SUCCESS
RESULT
is returned.
The original code now no longer relies on deref semantics, unfortunately.
) | ||
.ok() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should report over at windows-rs
that the naming choice of this function is less than ideal. I keep mistaking it for https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.ok which discards Error
information by turning Result
into Option
which instantly triggers during review.
use crate::auxil::dxgi::result::HResult; | ||
use std::ffi::CStr; | ||
use std::ptr; | ||
use std::path::PathBuf; | ||
use windows::{ | ||
core::{Interface, PCSTR, PCWSTR}, | ||
Win32::Graphics::Direct3D::{Dxc, Fxc}, | ||
}; | ||
|
||
pub(super) use dxc::{compile_dxc, get_dxc_container, DxcContainer}; | ||
use windows::Win32::Graphics::Direct3D; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't wgpu
use StdExternalCrate
formatting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem that we do.
fn new(lib_path: Option<PathBuf>, lib_name: &'static str) -> Result<Self, libloading::Error> { | ||
let lib_path = if let Some(lib_path) = lib_path { | ||
if lib_path.is_file() { | ||
lib_path | ||
} else { | ||
lib_path.join(lib_name) | ||
} | ||
} else { | ||
PathBuf::from(lib_name) | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't have copied this weird bit from hassle-rs
though, it has caused us quite a few headaches over the years.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can imagine, but I didn't want to change the behavior (yet) as it is documented in our docs.
impl OPCWSTR { | ||
fn new(s: &str) -> Self { | ||
let mut inner: Vec<_> = s.encode_utf16().collect(); | ||
inner.push(0); | ||
Self { inner } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't HSTRING
work for you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seemed to be even more complex for this purpose (ref-counted, no easy way to create it from a &str
, no easy way to get a PCWSTR
from it).
@@ -149,7 +149,6 @@ gpu-descriptor = "0.3" | |||
bit-set = "0.8" | |||
gpu-allocator = { version = "0.27", default-features = false } | |||
range-alloc = "0.1" | |||
hassle-rs = "0.11.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the abstraction by hassle-rs
isn't that useful. It's mostly creating the bindings but we have those now (except not for Linux 😭) in windows-rs
.
let mut result__ = None; | ||
|
||
(func)(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)) | ||
.ok() | ||
.into_device_result("GetDebugInterface")?; | ||
|
||
let mut result__ = core::ptr::null_mut(); | ||
Ok((func)(&Direct3D12::ID3D12Debug::IID, &mut result__) | ||
.and_then(|| unsafe { windows_core::Type::from_abi(result__) })) | ||
result__.ok_or(crate::DeviceError::Unexpected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right yes, that is unfortunate.
Perhaps this also has to do with SAL annotations, if I remember correctly there are different annotations that allow return values to be NULL
, versus them only being NULL
when a non-SUCCESS
RESULT
is returned.
The original code now no longer relies on deref semantics, unfortunately.
kind: Dxc::DXC_OUT_KIND, | ||
) -> Result<T, crate::DeviceError> { | ||
let mut result__: Option<T> = None; | ||
unsafe { res.GetOutput::<T>(kind, &mut None, <*mut _>::cast(&mut result__)) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could coerce because it takes a *mut Option<T>
unsafe { res.GetOutput::<T>(kind, &mut None, <*mut _>::cast(&mut result__)) } | |
unsafe { res.GetOutput::<T>(kind, &mut None, &mut result__) } |
res: &Dxc::IDxcResult, | ||
kind: Dxc::DXC_OUT_KIND, | ||
) -> Result<T, crate::DeviceError> { | ||
let mut result__: Option<T> = None; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May just be me who prefers this short-hand:
let mut result__: Option<T> = None; | |
let mut result__ = None::<T>; |
Although the type can be inferred:
let mut result__: Option<T> = None; | |
let mut result__ = None; |
Not that it matters too much, this signature will change when upstream metadata fixes land.
type Fun = extern "system" fn( | ||
rclsid: *const windows_core::GUID, | ||
riid: *const windows_core::GUID, | ||
ppv: *mut *mut core::ffi::c_void, | ||
) -> windows_core::HRESULT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to not use the existing DxcCreateInstanceProc
typedef? Because it's wrapped in Option<>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw that it exists but wasn't 100% sure how that would behave with libloading. It's not something I've seen in their docs/examples.
.compiler | ||
.Compile(&buffer, Some(&compile_args), None) | ||
} | ||
.into_device_result("Compile")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the old API, I wonder if Compile
returns an error code while IDxcResult
is also returned with valid errors (the root cause) in DXC_OUT_ERRORS
.
Or if that's still communicated by IDxcOperationResult::GetResult()
which you're not checking here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my testing, IDxcCompiler3::Compile
seems to succeed even if there was a compilation error.
Getting the blob via IDxcResult::GetOutput(DXC_OUT_ERRORS)
always succeeds. If there is a compilation error its size/length will be non 0 and will contain the error message.
The wiki seems to say that Compile
will only fail if the compiler itself ran into an internal error.
https://github.com/microsoft/DirectXShaderCompiler/wiki/Advanced-Error-Handling#internal-compiler-errors
.ok() | ||
// TODO: If there's a HRESULT, error may still be non-null and | ||
// contain info. | ||
.into_device_result("Root signature serialization")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was expecting a similar setup like Compile()
the error doesn't need to be OOM.
For Compile()
, I'm not sure which HRESULT
s to check either. There is:
- The result from
Compile()
; - Non-null
error
blob; - Null
blob
; - Result from
IDxcOperationResult::GetStatus()
.
(and any getters on IDxcOperationResult
/IDxcResult
can fail as well)
Connections
Follow-up to #5956.
Description
Mainly cleans-up usage of the D3D12 API.
Also fixes an issue where only high performance adapters would be enumerated if DXGI 1.6 is available.
I will have another follow-up to this that will go over and handle errors on a case-by-case basis (similar to #6119).
Testing
Existing tests. I also tested DXC locally with the changes to shader compilation.
PR doesn't need to be squashed, each commit builds by itself.