Add a test of D3D12 sharing resources

This commit is contained in:
baldurk
2020-01-21 18:28:57 +00:00
parent 1cb3cd5a1b
commit 9dc53d4dd1
12 changed files with 361 additions and 106 deletions
+7 -20
View File
@@ -45,10 +45,15 @@ COM_SMARTPTR(IDXGISurface);
COM_SMARTPTR(ID3D11Device);
COM_SMARTPTR(ID3D11Device1);
COM_SMARTPTR(ID3D11Device2);
COM_SMARTPTR(ID3D11Device3);
COM_SMARTPTR(ID3D11Device4);
COM_SMARTPTR(ID3D11Device5);
COM_SMARTPTR(ID3D11DeviceContext);
COM_SMARTPTR(ID3D11DeviceContext1);
COM_SMARTPTR(ID3D11DeviceContext2);
COM_SMARTPTR(ID3D11DeviceContext3);
COM_SMARTPTR(ID3D11DeviceContext4);
COM_SMARTPTR(ID3D11Multithread);
@@ -84,6 +89,8 @@ COM_SMARTPTR(ID3D11ShaderResourceView);
COM_SMARTPTR(ID3D11UnorderedAccessView);
COM_SMARTPTR(ID3D11DepthStencilView);
COM_SMARTPTR(ID3D11Fence);
COM_SMARTPTR(ID3D11InfoQueue);
COM_SMARTPTR(ID3DUserDefinedAnnotation);
@@ -230,26 +237,6 @@ protected:
D3D11_SAMPLER_DESC m_Desc;
};
enum class ResourceType
{
Buffer,
Texture1D,
Texture1DArray,
Texture2D,
Texture2DArray,
Texture2DMS,
Texture2DMSArray,
Texture3D,
};
enum class ViewType
{
SRV,
RTV,
DSV,
UAV,
};
class D3D11ViewCreator
{
public:
+13 -15
View File
@@ -260,17 +260,16 @@ GraphicsWindow *D3D11GraphicsTest::MakeWindow(int width, int height, const char
void D3D11GraphicsTest::PostDeviceCreate()
{
{
IDXGIDevicePtr pDXGIDevice;
HRESULT hr = dev->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
IDXGIDevicePtr pDXGIDevice = dev;
if(FAILED(hr) || !pDXGIDevice)
if(!pDXGIDevice)
{
TEST_ERROR("Couldn't get DXGI Device");
}
else
{
IDXGIAdapterPtr pDXGIAdapter;
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);
HRESULT hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);
if(FAILED(hr) || !pDXGIAdapter)
{
@@ -286,19 +285,18 @@ void D3D11GraphicsTest::PostDeviceCreate()
}
}
// if(d3d11_1)
{
dev->QueryInterface(__uuidof(ID3D11Device1), (void **)&dev1);
ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), (void **)&ctx1);
}
dev1 = dev;
dev2 = dev;
dev3 = dev;
dev4 = dev;
dev5 = dev;
// if(d3d11_2)
{
dev->QueryInterface(__uuidof(ID3D11Device2), (void **)&dev2);
ctx->QueryInterface(__uuidof(ID3D11DeviceContext2), (void **)&ctx2);
}
ctx1 = ctx;
ctx2 = ctx;
ctx3 = ctx;
ctx4 = ctx;
ctx->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), (void **)&annot);
annot = ctx;
}
void D3D11GraphicsTest::Shutdown()
+5
View File
@@ -181,10 +181,15 @@ struct D3D11GraphicsTest : public GraphicsTest
ID3D11DevicePtr dev;
ID3D11Device1Ptr dev1;
ID3D11Device2Ptr dev2;
ID3D11Device3Ptr dev3;
ID3D11Device4Ptr dev4;
ID3D11Device5Ptr dev5;
ID3D11DeviceContextPtr ctx;
ID3D11DeviceContext1Ptr ctx1;
ID3D11DeviceContext2Ptr ctx2;
ID3D11DeviceContext3Ptr ctx3;
ID3D11DeviceContext4Ptr ctx4;
ID3DUserDefinedAnnotationPtr annot;
private:
+9 -3
View File
@@ -354,6 +354,13 @@ D3D12TextureCreator &D3D12TextureCreator::CustomHeap(D3D12_HEAP_PROPERTIES heap)
return *this;
}
D3D12TextureCreator &D3D12TextureCreator::Shared()
{
m_HeapFlags = D3D12_HEAP_FLAG_SHARED;
m_TexDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
return *this;
}
D3D12TextureCreator &D3D12TextureCreator::InitialState(D3D12_RESOURCE_STATES state)
{
m_InitialState = state;
@@ -363,9 +370,8 @@ D3D12TextureCreator &D3D12TextureCreator::InitialState(D3D12_RESOURCE_STATES sta
D3D12TextureCreator::operator ID3D12ResourcePtr() const
{
ID3D12ResourcePtr tex;
CHECK_HR(m_Test->dev->CreateCommittedResource(&m_HeapDesc, D3D12_HEAP_FLAG_NONE, &m_TexDesc,
m_InitialState, NULL, __uuidof(ID3D12Resource),
(void **)&tex));
CHECK_HR(m_Test->dev->CreateCommittedResource(&m_HeapDesc, m_HeapFlags, &m_TexDesc, m_InitialState,
NULL, __uuidof(ID3D12Resource), (void **)&tex));
return tex;
}
+3 -20
View File
@@ -143,6 +143,8 @@ public:
D3D12TextureCreator &Readback();
D3D12TextureCreator &CustomHeap(D3D12_HEAP_PROPERTIES heap);
D3D12TextureCreator &Shared();
D3D12TextureCreator &InitialState(D3D12_RESOURCE_STATES state);
operator ID3D12ResourcePtr() const;
@@ -153,26 +155,7 @@ protected:
D3D12_RESOURCE_STATES m_InitialState;
D3D12_RESOURCE_DESC m_TexDesc;
D3D12_HEAP_PROPERTIES m_HeapDesc;
};
enum class ResourceType
{
Buffer,
Texture1D,
Texture1DArray,
Texture2D,
Texture2DArray,
Texture2DMS,
Texture2DMSArray,
Texture3D,
};
enum class ViewType
{
SRV,
RTV,
DSV,
UAV,
D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE;
};
class D3D12ViewCreator
+205
View File
@@ -0,0 +1,205 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "../d3d11/d3d11_test.h"
#include "d3d12_test.h"
RD_TEST(D3D12_Sharing, D3D12GraphicsTest)
{
static constexpr const char *Description =
"Tests D3D12 sharing between devices, both between D3D11 and D3D12 via shared handles "
"as well as making sure that multiple devices created on the same adapter are implicitly "
"identical.";
D3D11GraphicsTest d3d11;
void Prepare(int argc, char **argv)
{
d3d11.headless = true;
d3d11.Prepare(argc, argv);
D3D12GraphicsTest::Prepare(argc, argv);
if(m_12On7)
Avail = "Shared resources not implemented on D3D12On7";
}
int main()
{
// initialise, create window, create device, etc
if(!Init())
return 3;
if(!d3d11.Init())
return 4;
ID3D12DevicePtr dev2;
{
LUID luid = dev->GetAdapterLuid();
IDXGIAdapterPtr pDXGIAdapter;
HRESULT hr = EnumAdapterByLuid(dev->GetAdapterLuid(), pDXGIAdapter);
if(FAILED(hr))
return 2;
dev2 = CreateDevice(pDXGIAdapter);
if(!dev2)
return 2;
}
ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0");
ID3DBlobPtr psblob = Compile(D3DDefaultPixel, "main", "ps_4_0");
// share the VB from D3D11 to D3D12
const DefaultA2V tri[3] = {
{Vec3f(0.0f, 0.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
{Vec3f(3.0f, 0.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.0f, -3.0f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},
};
ID3D11BufferPtr d3d11vb = d3d11.MakeBuffer().Shared().Data(tri);
IDXGIResourcePtr dxgi = d3d11vb;
HANDLE handle = NULL;
dxgi->GetSharedHandle(&handle);
ID3D12ResourcePtr d3d12vb;
dev->OpenSharedHandle(handle, __uuidof(ID3D12Resource), (void **)&d3d12vb);
ID3D12RootSignaturePtr sig = MakeSig({});
// swap dev with dev2, to force pso to be created on the 'second' device (should be identical to
// the first). This may be completely redundant as we might have two identical pointers, but
// that's not guaranteed.
std::swap(dev, dev2);
ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);
// set them back
std::swap(dev, dev2);
ResourceBarrier(d3d12vb, D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
// share the 'backbuffer' texture with d3d11
ID3D12ResourcePtr d3d12tex =
MakeTexture(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, screenWidth / 2, screenHeight / 2)
.RTV()
.InitialState(D3D12_RESOURCE_STATE_COPY_SOURCE)
.Shared();
dev->CreateSharedHandle(d3d12tex, NULL, GENERIC_ALL, NULL, &handle);
ID3D11Texture2DPtr d3d11tex;
d3d11.dev1->OpenSharedResource1(handle, __uuidof(ID3D11Texture2D), (void **)&d3d11tex);
dev->CreateSharedHandle(m_GPUSyncFence, NULL, GENERIC_ALL, NULL, &handle);
ID3D11FencePtr d3d11fence;
d3d11.dev5->OpenSharedFence(handle, __uuidof(ID3D11Fence), (void **)&d3d11fence);
while(Running())
{
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
Reset(cmd);
ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D12_CPU_DESCRIPTOR_HANDLE rtv =
MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0);
ClearRenderTargetView(cmd, rtv, {1.0f, 0.0f, 0.0f, 1.0f});
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
IASetVertexBuffer(cmd, d3d12vb, sizeof(DefaultA2V), 0);
cmd->SetPipelineState(pso);
cmd->SetGraphicsRootSignature(sig);
RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});
RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight});
OMSetRenderTargets(cmd, {rtv}, {});
cmd->DrawInstanced(3, 1, 0, 0);
ResourceBarrier(cmd, d3d12tex, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON);
cmd->Close();
// submit (and set the fence value)
Submit({cmd});
// d3d11 waits on the fence, updates the offscreen texture, and signals the next fence
// value
d3d11.ctx4->Wait(d3d11fence, m_GPUSyncCounter);
D3D11_BOX box = {0, 0, 0, UINT(screenWidth / 2), UINT(screenHeight / 2), 1};
uint32_t *updateData = new uint32_t[box.right * box.bottom];
memset(updateData, 0xff, box.right * box.bottom * sizeof(uint32_t));
d3d11.ctx4->UpdateSubresource(d3d11tex, 0, &box, updateData, sizeof(uint32_t) * box.right,
sizeof(uint32_t) * box.right * box.bottom);
m_GPUSyncCounter++;
d3d11.ctx4->Signal(d3d11fence, m_GPUSyncCounter);
d3d11.ctx4->Flush();
// wait on the fence from d3d11's work then continue
queue->Wait(m_GPUSyncFence, m_GPUSyncCounter);
cmd = GetCommandBuffer();
Reset(cmd);
ResourceBarrier(cmd, d3d12tex, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
ResourceBarrier(cmd, bb, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON);
setMarker(cmd, "Copy");
// blit texture to the backbuffer
D3D12_TEXTURE_COPY_LOCATION dst = {bb, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX};
D3D12_TEXTURE_COPY_LOCATION src = {d3d12tex, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX};
cmd->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL);
OMSetRenderTargets(cmd, {rtv}, {});
FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_COPY_DEST);
cmd->Close();
Submit({cmd});
Present();
}
dev2 = NULL;
return 0;
}
};
REGISTER_TEST();
+56 -47
View File
@@ -41,36 +41,6 @@ HMODULE d3dcompiler = NULL;
IDXGIFactory1Ptr factory;
IDXGIAdapterPtr adapter;
bool d3d12on7 = false;
HRESULT EnumAdapterByLuid(LUID luid, IDXGIAdapterPtr &pAdapter)
{
HRESULT hr = S_OK;
pAdapter = NULL;
for(UINT i = 0; i < 10; i++)
{
IDXGIAdapterPtr ad;
hr = factory->EnumAdapters(i, &ad);
if(hr == S_OK && ad)
{
DXGI_ADAPTER_DESC desc;
ad->GetDesc(&desc);
if(desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart)
{
pAdapter = ad;
return S_OK;
}
}
else
{
break;
}
}
return E_FAIL;
}
};
void D3D12GraphicsTest::Prepare(int argc, char **argv)
@@ -141,6 +111,8 @@ void D3D12GraphicsTest::Prepare(int argc, char **argv)
else if(!factory)
Avail = "Couldn't create DXGI factory";
m_12On7 = d3d12on7;
for(int i = 0; i < argc; i++)
{
if(!strcmp(argv[i], "--gpuva") || !strcmp(argv[i], "--debug-gpu"))
@@ -210,18 +182,9 @@ bool D3D12GraphicsTest::Init()
}
}
{
HRESULT hr = S_OK;
hr = dyn_D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device),
(void **)&dev);
if(FAILED(hr))
{
TEST_ERROR("D3D12CreateDevice failed: %x", hr);
return false;
}
}
dev = CreateDevice(adapter);
if(!dev)
return false;
{
LUID luid = dev->GetAdapterLuid();
@@ -309,7 +272,7 @@ bool D3D12GraphicsTest::Init()
}
}
dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void **)&m_GPUSyncFence);
dev->CreateFence(0, D3D12_FENCE_FLAG_SHARED, __uuidof(ID3D12Fence), (void **)&m_GPUSyncFence);
m_GPUSyncHandle = ::CreateEvent(NULL, FALSE, FALSE, NULL);
m_GPUSyncFence->SetName(L"GPUSync fence");
@@ -413,7 +376,7 @@ bool D3D12GraphicsTest::Init()
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
};
dev->QueryInterface(__uuidof(ID3D12InfoQueue), (void **)&infoqueue);
infoqueue = dev;
if(infoqueue)
{
@@ -427,6 +390,50 @@ bool D3D12GraphicsTest::Init()
return true;
}
HRESULT D3D12GraphicsTest::EnumAdapterByLuid(LUID luid, IDXGIAdapterPtr &pAdapter)
{
HRESULT hr = S_OK;
pAdapter = NULL;
for(UINT i = 0; i < 10; i++)
{
IDXGIAdapterPtr ad;
hr = factory->EnumAdapters(i, &ad);
if(hr == S_OK && ad)
{
DXGI_ADAPTER_DESC desc;
ad->GetDesc(&desc);
if(desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart)
{
pAdapter = ad;
return S_OK;
}
}
else
{
break;
}
}
return E_FAIL;
}
ID3D12DevicePtr D3D12GraphicsTest::CreateDevice(IDXGIAdapterPtr a)
{
HRESULT hr = S_OK;
ID3D12DevicePtr ret;
hr = dyn_D3D12CreateDevice(a, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void **)&ret);
if(FAILED(hr))
TEST_ERROR("D3D12CreateDevice failed: %x", hr);
return ret;
}
GraphicsWindow *D3D12GraphicsTest::MakeWindow(int width, int height, const char *title)
{
return new Win32Window(width, height, title);
@@ -475,7 +482,8 @@ ID3D12ResourcePtr D3D12GraphicsTest::StartUsingBackbuffer(ID3D12GraphicsCommandL
{
ID3D12ResourcePtr bb = bbTex[texIdx];
ResourceBarrier(cmd, bbTex[texIdx], D3D12_RESOURCE_STATE_PRESENT, useState);
if(useState != D3D12_RESOURCE_STATE_PRESENT)
ResourceBarrier(cmd, bbTex[texIdx], D3D12_RESOURCE_STATE_PRESENT, useState);
return bbTex[texIdx];
}
@@ -485,7 +493,8 @@ void D3D12GraphicsTest::FinishUsingBackbuffer(ID3D12GraphicsCommandListPtr cmd,
{
ID3D12ResourcePtr bb = bbTex[texIdx];
ResourceBarrier(cmd, bbTex[texIdx], usedState, D3D12_RESOURCE_STATE_PRESENT);
if(usedState != D3D12_RESOURCE_STATE_PRESENT)
ResourceBarrier(cmd, bbTex[texIdx], usedState, D3D12_RESOURCE_STATE_PRESENT);
texIdx = 1 - texIdx;
}
@@ -498,7 +507,7 @@ void D3D12GraphicsTest::Submit(const std::vector<ID3D12GraphicsCommandListPtr> &
for(const ID3D12GraphicsCommandListPtr &cmd : cmds)
{
pendingCommandBuffers.push_back(std::make_pair(cmd, m_GPUSyncFence));
pendingCommandBuffers.push_back(std::make_pair(cmd, m_GPUSyncCounter));
submits.push_back(cmd);
}
+4 -1
View File
@@ -51,6 +51,9 @@ struct D3D12GraphicsTest : public GraphicsTest
void Shutdown();
GraphicsWindow *MakeWindow(int width, int height, const char *title);
HRESULT EnumAdapterByLuid(LUID luid, IDXGIAdapterPtr &pAdapter);
ID3D12DevicePtr CreateDevice(IDXGIAdapterPtr adapter);
enum BufType
{
eCBuffer = 0x0,
@@ -185,7 +188,7 @@ struct D3D12GraphicsTest : public GraphicsTest
ID3D12ResourcePtr bbTex[2];
uint32_t texIdx = 0;
bool gpuva = false;
bool gpuva = false, m_12On7 = false;
IDXGIFactory1Ptr m_Factory;
ID3D12DebugPtr d3d12Debug;
+1
View File
@@ -165,6 +165,7 @@
<ClCompile Include="d3d12\d3d12_overlay_test.cpp" />
<ClCompile Include="d3d12\d3d12_resource_lifetimes.cpp" />
<ClCompile Include="d3d12\d3d12_shader_editing.cpp" />
<ClCompile Include="d3d12\d3d12_sharing.cpp" />
<ClCompile Include="d3d12\d3d12_simple_triangle.cpp" />
<ClCompile Include="d3d12\d3d12_test.cpp" />
<ClCompile Include="d3d12\d3d12_texture_zoo.cpp" />
+3
View File
@@ -394,6 +394,9 @@
<ClCompile Include="vk\vk_mesh_zoo.cpp">
<Filter>Vulkan\demos</Filter>
</ClCompile>
<ClCompile Include="d3d12\d3d12_sharing.cpp">
<Filter>D3D12\demos</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="D3D11">
+21
View File
@@ -41,5 +41,26 @@ COM_SMARTPTR(IDXGIFactory1);
COM_SMARTPTR(IDXGIFactory4);
COM_SMARTPTR(IDXGIAdapter);
COM_SMARTPTR(IDXGISurface);
COM_SMARTPTR(IDXGIResource);
IDXGIAdapterPtr ChooseD3DAdapter(IDXGIFactoryPtr factory, int argc, char **argv, bool &warp);
enum class ResourceType
{
Buffer,
Texture1D,
Texture1DArray,
Texture2D,
Texture2DArray,
Texture2DMS,
Texture2DMSArray,
Texture3D,
};
enum class ViewType
{
SRV,
RTV,
DSV,
UAV,
};
+34
View File
@@ -0,0 +1,34 @@
import renderdoc as rd
import rdtest
class D3D12_Sharing(rdtest.TestCase):
demos_test_name = 'D3D12_Sharing'
def check_capture(self):
draw = self.find_draw("Copy")
draw: rd.DrawcallDescription = draw.next
self.controller.SetFrameEvent(draw.eventId, False)
pipe: rd.PipeState = self.controller.GetPipelineState()
# Should be white in the top left, green in the bottom right, and red elsewhere
self.check_pixel_value(draw.copyDestination, 0.2, 0.2, [1.0, 1.0, 1.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.3, 0.3, [1.0, 1.0, 1.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.4, 0.4, [1.0, 1.0, 1.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.6, 0.6, [0.0, 1.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.7, 0.7, [0.0, 1.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.8, 0.8, [0.0, 1.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.1, 0.6, [1.0, 0.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.1, 0.7, [1.0, 0.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.1, 0.8, [1.0, 0.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.6, 0.1, [1.0, 0.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.7, 0.1, [1.0, 0.0, 0.0, 1.0])
self.check_pixel_value(draw.copyDestination, 0.8, 0.1, [1.0, 0.0, 0.0, 1.0])
rdtest.log.success("Picked values are as expected")