Cannot create resource on DX12

  • Faweks1
  • Topic Author
More
1 year 7 months ago #1 by Faweks1 Cannot create resource on DX12 was created by Faweks1
In any DX12 game resource creation returns 0.

static resource res;
const resource_desc desc = device->get_resource_desc(back_buffer);   // back_buffer is not multisampled
const format format = format_to_default_typed(desc.texture.format, 0);
bool result = device->create_resource(
        resource_desc(desc.texture.width, desc.texture.height, 1, 1, format, 1, memory_heap::gpu_to_cpu, resource_usage::copy_dest),
        nullptr,
        resource_usage::cpu_access,
        &res
);
// result is 0 for DX12

Is there anything wrong with above code?

Please Log in or Create an account to join the conversation.

  • crosire
More
1 year 7 months ago - 1 year 7 months ago #2 by crosire Replied by crosire on topic Cannot create resource on DX12
Resources created in the `memory_heap::gpu_to_cpu` heap must be created with initial state `resource_usage::copy_dest` (rather than `resource_usage::cpu_access`) and cannot be changed away from that state in DX12 (as per docs.microsoft.com/en-us/windows/win32/a...3d12-d3d12_heap_type entry for `D3D12_HEAP_TYPE_READBACK`, which is what `memory_heap::gpu_to_cpu` translates to).

You can also enable the DX12 debug layer which will print an error to debug output for cases like this. Might help debugging. To do so, run dxcpl.exe (e.g. via Win + R), add your app executable to the list and then set "Debug Layer" to "Force On". Just remember to disable that again after debugging, since it impacts performance negatively.
Last edit: 1 year 7 months ago by crosire.
The following user(s) said Thank You: Faweks1

Please Log in or Create an account to join the conversation.

  • Faweks1
  • Topic Author
More
1 year 7 months ago - 1 year 7 months ago #3 by Faweks1 Replied by Faweks1 on topic Cannot create resource on DX12
Sorry for late response, i was off for a while. Thank you for given info, i created resource and mapped data into it without problems now.
But there is another question.
When i try to copy buffer data from my resource into back buffer it fails for DX12 only, all DX 9-11 works as expected.
I'm using on_present callback with following code fragment:

    cmd_list->barrier(host_buffer, resource_usage::copy_dest, resource_usage::copy_source);
    cmd_list->barrier(back_buffer, resource_usage::copy_source, resource_usage::copy_dest);
    cmd_list->copy_resource(host_buffer, back_buffer); // Tested also copy_texture_to_buffer and copy_texture_region.
    cmd_list->barrier(host_buffer, resource_usage::copy_source, resource_usage::copy_dest);
    cmd_list->barrier(back_buffer, resource_usage::copy_dest, resource_usage::present);

For DX12 i tested multiple resources with multiple heap settings (mostly cpu_to_gpu for upload) and cannot make this work.
After some debugging i found that i cannot set back_buffer usage to copy_dest - no matter how i write code.
cmd_list->barrier(back_buffer, resource_usage::copy_source, resource_usage::copy_dest); // This always fails on DX12 for any old state.

Do i have to bind new viewport and render targets or can i workaround it somehow?
Last edit: 1 year 7 months ago by Faweks1.

Please Log in or Create an account to join the conversation.

  • crosire
More
1 year 7 months ago #4 by crosire Replied by crosire on topic Cannot create resource on DX12
Your second barrier presumably should be
cmd_list->barrier(back_buffer, resource_usage::present, resource_usage::copy_dest);
unless you changed to copy_source at some point before already.

But that wouldn't change much. I don't see a problem with copying into the back buffer in DX12 (did a quick test and it worked).
So you are probably having a synchronization problem: E.g. putting data into a host resource, then kicking off the copy to the back buffer, only to re-use the same host resource soon after and put new data into it. This will not work, since at this point, the GPU has very likely not started/finished performing the actual copy yet, so overwriting the data in the source resource causes it to use that new data when it actually comes around to performing the copy, rather than the data you had intended to upload.

Keep in mind that CPU and GPU run asynchronously and when you add a command to a command-list by calling "command_list::copy_resource", "command_list::draw", etc. then they aren't executed by the GPU immediately. They are executed after that command list was queued up on the GPU (which you can force with "command_queue::flush_immediate_command_list"). And then one would still have to wait for execution to finish on the GPU (e.g. with "command_queue::wait_idle").

A better alternative to waiting for the GPU to finish would be to use multiple host resources and iterate through them over multiple frames. So map and fill resource 1 in the first frame, then issue a copy command using that resource as source; in the second frame, map and fill resource 2 and use that as copy source, ... and at some point go back to resource 1 again (e.g. after 3 frames / 3 host resources, since it is likely that the GPU has caught up with the commands the CPU issued after 3 frames).
DX11 does this for you behind the scenes with a single resource, which is why it looks like things are working. DX12 does not do this automatically and you have to do it yourself (and it's probably better to just do it always, regardless of the graphics API, to be safe).
The following user(s) said Thank You: Faweks1

Please Log in or Create an account to join the conversation.

  • Faweks1
  • Topic Author
More
1 year 7 months ago #5 by Faweks1 Replied by Faweks1 on topic Cannot create resource on DX12
Thanks, i'll test multiple resources approach.

And yes i've changed back_buffer state to copy_source before in code to map it to resource.

Please Log in or Create an account to join the conversation.

  • Faweks1
  • Topic Author
More
1 year 7 months ago #6 by Faweks1 Replied by Faweks1 on topic Cannot create resource on DX12
I did some more digging. Now i can change back_buffer state to copy_dest but after that dx12 will fail with DXGI_ERROR_ACCESS_DENIED error while executing command queue.

I attached code that gives this error. I'm using on_present callback and buffer_to_texture copy (checked with device->check_capability(device_caps::copy_buffer_to_texture)).

Minimal reproducible code (i know it's single resource here, but with multiple effect is the same):
static void on_present(command_queue* queue, swapchain* swapchain, const rect*, const rect*, uint32_t, const rect*)
{
    device* const device = swapchain->get_device();
    resource back_buffer = swapchain->get_current_back_buffer();

    const resource_desc desc = device->get_resource_desc(back_buffer);
    format format = format_to_default_typed(desc.texture.format, 0);
    int32_t row_pitch = format_row_pitch(format, desc.texture.width);
    row_pitch = (row_pitch + 255) & ~255;
    const uint32_t slice_pitch = format_slice_pitch(format, row_pitch, desc.texture.height);

    resource test;
    bool create_result = device->create_resource(resource_desc(slice_pitch, memory_heap::cpu_to_gpu, resource_usage::cpu_access), nullptr, resource_usage::cpu_access, &test);
    stringstream s1;
    s1 << "create_result=" << create_result;
    reshade::log_message(3, s1.str().c_str());

    subresource_data subres = {};
    bool map_result = device->map_buffer_region(test, 0, UINT64_MAX, map_access::read_write, &subres.data);
    stringstream s2;
    s2 << "map_result=" << map_result;
    reshade::log_message(3, s2.str().c_str());
    subres.row_pitch = row_pitch;

    // Maximize red or blue channel, doesn't matter
    auto mapped_data = static_cast<uint8_t*>(subres.data);
    for (uint32_t y = 0; y < desc.texture.height; ++y)
    {
        switch (format) {
        case format::r8g8b8a8_unorm:
        case format::r8g8b8x8_unorm:
        case format::b8g8r8a8_unorm:
        case format::b8g8r8x8_unorm:
            for (uint32_t x = 0; x < desc.texture.width; ++x)
            {
                const uint32_t px_index = y * row_pitch + x * 4;
                mapped_data[px_index] = uint8_t(255);
            }
            break;
        case format::b10g10r10a2_unorm:
        case format::r10g10b10a2_unorm:
            for (uint32_t x = 0; x < desc.texture.width; ++x)
            {
                const uint32_t px_index = y * row_pitch + x * 4;
                uint32_t color = 3 << 30;
                color += uint8_t(255) * 4;
                mapped_data[px_index] = color;
            }
            break;
        }
    }

    device->unmap_buffer_region(test);

    stringstream s3;
    s3 << "Copy attempt:";
    reshade::log_message(3, s3.str().c_str());

    command_list* const cmd_list = queue->get_immediate_command_list();
    cmd_list->barrier(back_buffer, resource_usage::present, resource_usage::copy_dest);
    cmd_list->copy_resource(test, back_buffer);
    cmd_list->barrier(back_buffer, resource_usage::copy_dest, resource_usage::present);
    queue->flush_immediate_command_list();
    queue->wait_idle();
}

Logs:
22:48:40:448 [24928] | INFO  | create_result=1
22:48:40:448 [24928] | INFO  | map_result=1
22:48:40:449 [24928] | INFO  | Copy attempt:
22:48:40:458 [24928] | ERROR | Device was lost with DXGI_ERROR_DEVICE_REMOVED! Destroying all resources and disabling ReShade.
22:48:40:458 [24928] | ERROR | > Device removal reason is 0x887a002b.

Please Log in or Create an account to join the conversation.

  • crosire
More
1 year 7 months ago - 1 year 7 months ago #7 by crosire Replied by crosire on topic Cannot create resource on DX12
You found a bug in ReShade ... "get_current_back_buffer" doesn't currently return the correct resource in the "present" event (it only does so in later events like "reshade_present"). Because of this the code is accessing the wrong back buffer resource, which is what DX is complaining about. This is fixed in github.com/crosire/reshade/commit/d2d9ae...b8971c3d66bf01deec28 .

But you also have a bug in your code: "cmd_list->copy_resource(test, back_buffer);" should be "cmd_list->copy_buffer_to_texture(test, 0, 0, 0, back_buffer, 0);", since "copy_resource" requires both resources to be of the same type as per D3D12 spec.

If in addition you make the "row_pitch = (row_pitch + 255) & ~255;" line conditional to only occur in D3D12 (since only D3D12 has that annoying requirement that texture rows have to be aligned on 256 bytes) and change the "map_buffer_region" call to only request write access via "map_access:write_only" (since the "cpu_to_gpu" heap is write only, and other map access types will therefore fail in OpenGL etc.), then this code works for D3D12, OpenGL and Vulkan (it unfortunately does not work for D3D9/10/11, since they do not implement "copy_buffer_to_texture").
static void on_present(command_queue *queue, swapchain *swapchain, const rect *, const rect *, uint32_t, const rect *)
{
	device *const device = swapchain->get_device();
	resource back_buffer = swapchain->get_current_back_buffer();

	const resource_desc desc = device->get_resource_desc(back_buffer);
	format format = format_to_default_typed(desc.texture.format, 0);
	int32_t row_pitch = format_row_pitch(format, desc.texture.width);
	if (device->get_api() == device_api::d3d12)
		row_pitch = (row_pitch + 255) & ~255;
	const uint32_t slice_pitch = format_slice_pitch(format, row_pitch, desc.texture.height);

	resource test;
	bool create_result = device->create_resource(resource_desc(slice_pitch, memory_heap::cpu_to_gpu, resource_usage::cpu_access), nullptr, resource_usage::cpu_access, &test);
	std::stringstream s1;
	s1 << "create_result=" << create_result;
	reshade::log_message(3, s1.str().c_str());

	subresource_data subres = {};
	bool map_result = device->map_buffer_region(test, 0, UINT64_MAX, map_access::write_only, &subres.data);
	std::stringstream s2;
	s2 << "map_result=" << map_result;
	reshade::log_message(3, s2.str().c_str());
	subres.row_pitch = row_pitch;

	// Maximize red or blue channel, doesn't matter
	auto mapped_data = static_cast<uint8_t *>(subres.data);
	for (uint32_t y = 0; y < desc.texture.height; ++y)
	{
		switch (format) {
		case format::r8g8b8a8_unorm:
		case format::r8g8b8x8_unorm:
		case format::b8g8r8a8_unorm:
		case format::b8g8r8x8_unorm:
			for (uint32_t x = 0; x < desc.texture.width; ++x)
			{
				const uint32_t px_index = y * row_pitch + x * 4;
				mapped_data[px_index] = uint8_t(255);
			}
			break;
		case format::b10g10r10a2_unorm:
		case format::r10g10b10a2_unorm:
			for (uint32_t x = 0; x < desc.texture.width; ++x)
			{
				const uint32_t px_index = y * row_pitch + x * 4;
				uint32_t color = 3 << 30;
				color += uint8_t(255) * 4;
				mapped_data[px_index] = color;
			}
			break;
		}
	}

	device->unmap_buffer_region(test);

	std::stringstream s3;
	s3 << "Copy attempt:";
	reshade::log_message(3, s3.str().c_str());

	command_list *const cmd_list = queue->get_immediate_command_list();
	cmd_list->barrier(back_buffer, resource_usage::present, resource_usage::copy_dest);
	cmd_list->copy_buffer_to_texture(test, 0, 0, 0, back_buffer, 0);
	cmd_list->barrier(back_buffer, resource_usage::copy_dest, resource_usage::present);
	queue->flush_immediate_command_list();
	queue->wait_idle();
}
Last edit: 1 year 7 months ago by crosire.
The following user(s) said Thank You: Faweks1

Please Log in or Create an account to join the conversation.

  • Faweks1
  • Topic Author
More
1 year 7 months ago #8 by Faweks1 Replied by Faweks1 on topic Cannot create resource on DX12
Thank you! Now everything works as expected :)

Please Log in or Create an account to join the conversation.

We use cookies
We use cookies on our website. Some of them are essential for the operation of the forum. You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.