#include <iostream>
using namespace std;
int GCD(const int& a, const int& b)
{
return (b == 0) ? a : GCD(b, a % b);
}
int main()
{
int testcase = 0, a = 0, b = 0, min = 0, gcd = 0;
cin >> testcase;
while (testcase-- > 0)
{
cin >> a >> b;
while (a != 1)
{
min = (b + a - 1) / a;
a = a * min - b;
b *= min;
gcd = GCD(a, b);
a /= gcd;
b /= gcd;
}
cout << b << endl;
}
return 0;
}
그 다음 보이는 것은 Platform::String의 reference 형으로 선언 된 전역 변수이다.
// Indices into the application state map.
Platform::String^ AngleKey = "Angle";
Platform::String^ TrackingKey = "Tracking";
application state map의 Index들이라고 설명이 되어 있는데, 뒤에 사용처가 나온다면 좀 더 알아보자.
// Loads vertex and pixel shaders from files and instantiates the cube geometry.
Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
m_loadingComplete(false),
m_radiansPerSecond(XM_PIDIV4), // rotate 45 degrees per second
m_angle(0),
m_tracking(false),
m_mappedConstantBuffer(nullptr),
m_deviceResources(deviceResources)
{
LoadState();
ZeroMemory(&m_constantBufferData, sizeof(m_constantBufferData));
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
생성자이다. 반드시 DeviceResources를 입력 받아야 한다.
생성자에서는 Rendering loop에 관한 변수들이 초기화 되고 있다.
매 프레임마다 정보가 갱신 되었는지 확인해주는 m_loadingComplete는 아직 생성자 단계에서는 false로.
회전 속도는 π/4(rad). 초기 회전각은 0도. tracking을 확인하는 m_tracking도 생성자 단계에서는 false이다.
유일하게 Cube 회전에 관련되지 않은 것은 m_mappedConstantBuffer이다.
이에 대해서는 뒤에 자세한 설명을 하겠다.
생성자 내부에서는 LoadState()를 호출하고, m_constantBufferData를 0으로 초기화 한다.
// Restores the previous state of the renderer.
void Sample3DSceneRenderer::LoadState()
{
auto state = ApplicationData::Current->LocalSettings->Values;
if (state->HasKey(AngleKey))
{
m_angle = safe_cast<IPropertyValue^>(state->Lookup(AngleKey))->GetSingle();
state->Remove(AngleKey);
}
if (state->HasKey(TrackingKey))
{
m_tracking = safe_cast<IPropertyValue^>(state->Lookup(TrackingKey))->GetBoolean();
state->Remove(TrackingKey);
}
}
LoadState는 이전 renderer 상태를 복구 시키는 함수이다.
이전 상태에서의 localSettings 값을 받아와, 이 값이 AngleKey일 경우에 m_angle 값을 저장하고,
이전 값이 TrackingKey일 경우에는 m_tracking 상태를 저장한다.
이 뒤에 CreateDeviceDependentResources()를 호출한다.
void Sample3DSceneRenderer::CreateDeviceDependentResources()
{
auto d3dDevice = m_deviceResources->GetD3DDevice();
// Create a root signature with a single constant buffer slot.
{
CD3DX12_DESCRIPTOR_RANGE range;
CD3DX12_ROOT_PARAMETER parameter;
range.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
parameter.InitAsDescriptorTable(1, &range, D3D12_SHADER_VISIBILITY_VERTEX);
D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | // Only the input assembler stage needs access to the constant buffer.
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
CD3DX12_ROOT_SIGNATURE_DESC descRootSignature;
descRootSignature.Init(1, ¶meter, 0, nullptr, rootSignatureFlags);
ComPtr<ID3DBlob> pSignature;
ComPtr<ID3DBlob> pError;
DX::ThrowIfFailed(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, pSignature.GetAddressOf(), pError.GetAddressOf()));
DX::ThrowIfFailed(d3dDevice->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
NAME_D3D12_OBJECT(m_rootSignature);
}
// Load shaders asynchronously.
auto createVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso").then([this](std::vector<byte>& fileData) {
m_vertexShader = fileData;
});
auto createPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso").then([this](std::vector<byte>& fileData) {
m_pixelShader = fileData;
});
// Create the pipeline state once the shaders are loaded.
auto createPipelineStateTask = (createPSTask && createVSTask).then([this]() {
static const D3D12_INPUT_ELEMENT_DESC inputLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
D3D12_GRAPHICS_PIPELINE_STATE_DESC state = {};
state.InputLayout = { inputLayout, _countof(inputLayout) };
state.pRootSignature = m_rootSignature.Get();
state.VS = CD3DX12_SHADER_BYTECODE(&m_vertexShader[0], m_vertexShader.size());
state.PS = CD3DX12_SHADER_BYTECODE(&m_pixelShader[0], m_pixelShader.size());
state.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
state.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
state.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
state.SampleMask = UINT_MAX;
state.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
state.NumRenderTargets = 1;
state.RTVFormats[0] = m_deviceResources->GetBackBufferFormat();
state.DSVFormat = m_deviceResources->GetDepthBufferFormat();
state.SampleDesc.Count = 1;
DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateGraphicsPipelineState(&state, IID_PPV_ARGS(&m_pipelineState)));
// Shader data can be deleted once the pipeline state is created.
m_vertexShader.clear();
m_pixelShader.clear();
});
// Create and upload cube geometry resources to the GPU.
auto createAssetsTask = createPipelineStateTask.then([this]() {
auto d3dDevice = m_deviceResources->GetD3DDevice();
// Create a command list.
DX::ThrowIfFailed(d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_deviceResources->GetCommandAllocator(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));
NAME_D3D12_OBJECT(m_commandList);
// Cube vertices. Each vertex has a position and a color.
VertexPositionColor cubeVertices[] =
{
{ XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f) },
{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f) },
{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f) },
};
const UINT vertexBufferSize = sizeof(cubeVertices);
// Create the vertex buffer resource in the GPU's default heap and copy vertex data into it using the upload heap.
// The upload resource must not be released until after the GPU has finished using it.
Microsoft::WRL::ComPtr<ID3D12Resource> vertexBufferUpload;
CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT);
CD3DX12_RESOURCE_DESC vertexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&vertexBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)));
CD3DX12_HEAP_PROPERTIES uploadHeapProperties(D3D12_HEAP_TYPE_UPLOAD);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&vertexBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&vertexBufferUpload)));
NAME_D3D12_OBJECT(m_vertexBuffer);
// Upload the vertex buffer to the GPU.
{
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(cubeVertices);
vertexData.RowPitch = vertexBufferSize;
vertexData.SlicePitch = vertexData.RowPitch;
UpdateSubresources(m_commandList.Get(), m_vertexBuffer.Get(), vertexBufferUpload.Get(), 0, 0, 1, &vertexData);
CD3DX12_RESOURCE_BARRIER vertexBufferResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
m_commandList->ResourceBarrier(1, &vertexBufferResourceBarrier);
}
// Load mesh indices. Each trio of indices represents a triangle to be rendered on the screen.
// For example: 0,2,1 means that the vertices with indexes 0, 2 and 1 from the vertex buffer compose the
// first triangle of this mesh.
unsigned short cubeIndices[] =
{
0, 2, 1, // -x
1, 2, 3,
4, 5, 6, // +x
5, 7, 6,
0, 1, 5, // -y
0, 5, 4,
2, 6, 7, // +y
2, 7, 3,
0, 4, 6, // -z
0, 6, 2,
1, 3, 7, // +z
1, 7, 5,
};
const UINT indexBufferSize = sizeof(cubeIndices);
// Create the index buffer resource in the GPU's default heap and copy index data into it using the upload heap.
// The upload resource must not be released until after the GPU has finished using it.
Microsoft::WRL::ComPtr<ID3D12Resource> indexBufferUpload;
CD3DX12_RESOURCE_DESC indexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(indexBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_indexBuffer)));
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&indexBufferUpload)));
NAME_D3D12_OBJECT(m_indexBuffer);
// Upload the index buffer to the GPU.
{
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = reinterpret_cast<BYTE*>(cubeIndices);
indexData.RowPitch = indexBufferSize;
indexData.SlicePitch = indexData.RowPitch;
UpdateSubresources(m_commandList.Get(), m_indexBuffer.Get(), indexBufferUpload.Get(), 0, 0, 1, &indexData);
CD3DX12_RESOURCE_BARRIER indexBufferResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER);
m_commandList->ResourceBarrier(1, &indexBufferResourceBarrier);
}
// Create a descriptor heap for the constant buffers.
{
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = DX::c_frameCount;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// This flag indicates that this descriptor heap can be bound to the pipeline and that descriptors contained in it can be referenced by a root table.
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
DX::ThrowIfFailed(d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_cbvHeap)));
NAME_D3D12_OBJECT(m_cbvHeap);
}
CD3DX12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(DX::c_frameCount * c_alignedConstantBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&constantBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_constantBuffer)));
NAME_D3D12_OBJECT(m_constantBuffer);
// Create constant buffer views to access the upload buffer.
D3D12_GPU_VIRTUAL_ADDRESS cbvGpuAddress = m_constantBuffer->GetGPUVirtualAddress();
CD3DX12_CPU_DESCRIPTOR_HANDLE cbvCpuHandle(m_cbvHeap->GetCPUDescriptorHandleForHeapStart());
m_cbvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (int n = 0; n < DX::c_frameCount; n++)
{
D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
desc.BufferLocation = cbvGpuAddress;
desc.SizeInBytes = c_alignedConstantBufferSize;
d3dDevice->CreateConstantBufferView(&desc, cbvCpuHandle);
cbvGpuAddress += desc.SizeInBytes;
cbvCpuHandle.Offset(m_cbvDescriptorSize);
}
// Map the constant buffers.
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
DX::ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&m_mappedConstantBuffer)));
ZeroMemory(m_mappedConstantBuffer, DX::c_frameCount * c_alignedConstantBufferSize);
// We don't unmap this until the app closes. Keeping things mapped for the lifetime of the resource is okay.
// Close the command list and execute it to begin the vertex/index buffer copy into the GPU's default heap.
DX::ThrowIfFailed(m_commandList->Close());
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// Create vertex/index buffer views.
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(VertexPositionColor);
m_vertexBufferView.SizeInBytes = sizeof(cubeVertices);
m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
m_indexBufferView.SizeInBytes = sizeof(cubeIndices);
m_indexBufferView.Format = DXGI_FORMAT_R16_UINT;
// Wait for the command list to finish executing; the vertex/index buffers need to be uploaded to the GPU before the upload resources go out of scope.
m_deviceResources->WaitForGpu();
});
createAssetsTask.then([this]() {
m_loadingComplete = true;
});
}
상당히 긴 코드인데, 하나하나 살펴보자.
먼저 D3DDevice 객체를 받아오고, single constant buffer slot에 root signature를 생성한다.
// Create the pipeline state once the shaders are loaded.
auto createPipelineStateTask = (createPSTask && createVSTask).then([this]() {
static const D3D12_INPUT_ELEMENT_DESC inputLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
D3D12_GRAPHICS_PIPELINE_STATE_DESC state = {};
state.InputLayout = { inputLayout, _countof(inputLayout) };
state.pRootSignature = m_rootSignature.Get();
state.VS = CD3DX12_SHADER_BYTECODE(&m_vertexShader[0], m_vertexShader.size());
state.PS = CD3DX12_SHADER_BYTECODE(&m_pixelShader[0], m_pixelShader.size());
state.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
state.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
state.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
state.SampleMask = UINT_MAX;
state.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
state.NumRenderTargets = 1;
state.RTVFormats[0] = m_deviceResources->GetBackBufferFormat();
state.DSVFormat = m_deviceResources->GetDepthBufferFormat();
state.SampleDesc.Count = 1;
DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateGraphicsPipelineState(&state, IID_PPV_ARGS(&m_pipelineState)));
// Shader data can be deleted once the pipeline state is created.
m_vertexShader.clear();
m_pixelShader.clear();
});
이렇게 생성된 Pipeline에는 Cube Geometry Resources를 생성하고, GPU로 불러오는 작업이 차례대로 들어간다.
auto createAssetsTask = createPipelineStateTask.then([this]() {
auto d3dDevice = m_deviceResources->GetD3DDevice();
// Create a command list.
DX::ThrowIfFailed(d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_deviceResources->GetCommandAllocator(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));
NAME_D3D12_OBJECT(m_commandList);
// Cube vertices. Each vertex has a position and a color.
VertexPositionColor cubeVertices[] =
{
{ XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f) },
{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f) },
{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f) },
};
const UINT vertexBufferSize = sizeof(cubeVertices);
// Create the vertex buffer resource in the GPU's default heap and copy vertex data into it using the upload heap.
// The upload resource must not be released until after the GPU has finished using it.
Microsoft::WRL::ComPtr<ID3D12Resource> vertexBufferUpload;
CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT);
CD3DX12_RESOURCE_DESC vertexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&vertexBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)));
CD3DX12_HEAP_PROPERTIES uploadHeapProperties(D3D12_HEAP_TYPE_UPLOAD);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&vertexBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&vertexBufferUpload)));
NAME_D3D12_OBJECT(m_vertexBuffer);
// Upload the vertex buffer to the GPU.
{
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(cubeVertices);
vertexData.RowPitch = vertexBufferSize;
vertexData.SlicePitch = vertexData.RowPitch;
UpdateSubresources(m_commandList.Get(), m_vertexBuffer.Get(), vertexBufferUpload.Get(), 0, 0, 1, &vertexData);
CD3DX12_RESOURCE_BARRIER vertexBufferResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
m_commandList->ResourceBarrier(1, &vertexBufferResourceBarrier);
}
// Load mesh indices. Each trio of indices represents a triangle to be rendered on the screen.
// For example: 0,2,1 means that the vertices with indexes 0, 2 and 1 from the vertex buffer compose the
// first triangle of this mesh.
unsigned short cubeIndices[] =
{
0, 2, 1, // -x
1, 2, 3,
4, 5, 6, // +x
5, 7, 6,
0, 1, 5, // -y
0, 5, 4,
2, 6, 7, // +y
2, 7, 3,
0, 4, 6, // -z
0, 6, 2,
1, 3, 7, // +z
1, 7, 5,
};
const UINT indexBufferSize = sizeof(cubeIndices);
// Create the index buffer resource in the GPU's default heap and copy index data into it using the upload heap.
// The upload resource must not be released until after the GPU has finished using it.
Microsoft::WRL::ComPtr<ID3D12Resource> indexBufferUpload;
CD3DX12_RESOURCE_DESC indexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(indexBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_indexBuffer)));
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&indexBufferUpload)));
NAME_D3D12_OBJECT(m_indexBuffer);
// Upload the index buffer to the GPU.
{
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = reinterpret_cast<BYTE*>(cubeIndices);
indexData.RowPitch = indexBufferSize;
indexData.SlicePitch = indexData.RowPitch;
UpdateSubresources(m_commandList.Get(), m_indexBuffer.Get(), indexBufferUpload.Get(), 0, 0, 1, &indexData);
CD3DX12_RESOURCE_BARRIER indexBufferResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER);
m_commandList->ResourceBarrier(1, &indexBufferResourceBarrier);
}
// Create a descriptor heap for the constant buffers.
{
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = DX::c_frameCount;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// This flag indicates that this descriptor heap can be bound to the pipeline and that descriptors contained in it can be referenced by a root table.
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
DX::ThrowIfFailed(d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_cbvHeap)));
NAME_D3D12_OBJECT(m_cbvHeap);
}
CD3DX12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(DX::c_frameCount * c_alignedConstantBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&constantBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_constantBuffer)));
NAME_D3D12_OBJECT(m_constantBuffer);
// Create constant buffer views to access the upload buffer.
D3D12_GPU_VIRTUAL_ADDRESS cbvGpuAddress = m_constantBuffer->GetGPUVirtualAddress();
CD3DX12_CPU_DESCRIPTOR_HANDLE cbvCpuHandle(m_cbvHeap->GetCPUDescriptorHandleForHeapStart());
m_cbvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (int n = 0; n < DX::c_frameCount; n++)
{
D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
desc.BufferLocation = cbvGpuAddress;
desc.SizeInBytes = c_alignedConstantBufferSize;
d3dDevice->CreateConstantBufferView(&desc, cbvCpuHandle);
cbvGpuAddress += desc.SizeInBytes;
cbvCpuHandle.Offset(m_cbvDescriptorSize);
}
// Map the constant buffers.
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
DX::ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&m_mappedConstantBuffer)));
ZeroMemory(m_mappedConstantBuffer, DX::c_frameCount * c_alignedConstantBufferSize);
// We don't unmap this until the app closes. Keeping things mapped for the lifetime of the resource is okay.
// Close the command list and execute it to begin the vertex/index buffer copy into the GPU's default heap.
DX::ThrowIfFailed(m_commandList->Close());
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// Create vertex/index buffer views.
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(VertexPositionColor);
m_vertexBufferView.SizeInBytes = sizeof(cubeVertices);
m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
m_indexBufferView.SizeInBytes = sizeof(cubeIndices);
m_indexBufferView.Format = DXGI_FORMAT_R16_UINT;
// Wait for the command list to finish executing; the vertex/index buffers need to be uploaded to the GPU before the upload resources go out of scope.
m_deviceResources->WaitForGpu();
});
// Load mesh indices. Each trio of indices represents a triangle to be rendered on the screen.
// For example: 0,2,1 means that the vertices with indexes 0, 2 and 1 from the vertex buffer compose the
// first triangle of this mesh.
unsigned short cubeIndices[] =
{
0, 2, 1, // -x
1, 2, 3,
4, 5, 6, // +x
5, 7, 6,
0, 1, 5, // -y
0, 5, 4,
2, 6, 7, // +y
2, 7, 3,
0, 4, 6, // -z
0, 6, 2,
1, 3, 7, // +z
1, 7, 5,
};
const UINT indexBufferSize = sizeof(cubeIndices);
Mesh Index가 모두 선언 되면 GPU의 default heap에 이 index들을 담을 buffer를 생성하고,
upload heap을 통해 값을 복사한다. uplaod resource는 vertex와 마찬가지로 GPU에서 사용을 마친 후 반드시 해제되어야 한다.
// Create the index buffer resource in the GPU's default heap and copy index data into it using the upload heap.
// The upload resource must not be released until after the GPU has finished using it.
Microsoft::WRL::ComPtr<ID3D12Resource> indexBufferUpload;
CD3DX12_RESOURCE_DESC indexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(indexBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_indexBuffer)));
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&indexBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&indexBufferUpload)));
NAME_D3D12_OBJECT(m_indexBuffer);
// Upload the index buffer to the GPU.
{
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = reinterpret_cast<BYTE*>(cubeIndices);
indexData.RowPitch = indexBufferSize;
indexData.SlicePitch = indexData.RowPitch;
UpdateSubresources(m_commandList.Get(), m_indexBuffer.Get(), indexBufferUpload.Get(), 0, 0, 1, &indexData);
CD3DX12_RESOURCE_BARRIER indexBufferResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER);
m_commandList->ResourceBarrier(1, &indexBufferResourceBarrier);
}
Index까지 모두 GPU의 Default Heap에 복사되면 constant buffer를 위한 descriptor heap을 생성한다.
// Create a descriptor heap for the constant buffers.
{
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = DX::c_frameCount;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// This flag indicates that this descriptor heap can be bound to the pipeline and that descriptors contained in it can be referenced by a root table.
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
DX::ThrowIfFailed(d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_cbvHeap)));
NAME_D3D12_OBJECT(m_cbvHeap);
}
CD3DX12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(DX::c_frameCount * c_alignedConstantBufferSize);
DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&constantBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_constantBuffer)));
NAME_D3D12_OBJECT(m_constantBuffer);
그리고 upload buffer에 접속하기 위한 constant buffer view를 생성하고, constant buffer를 연결시킨다.
// Create constant buffer views to access the upload buffer.
D3D12_GPU_VIRTUAL_ADDRESS cbvGpuAddress = m_constantBuffer->GetGPUVirtualAddress();
CD3DX12_CPU_DESCRIPTOR_HANDLE cbvCpuHandle(m_cbvHeap->GetCPUDescriptorHandleForHeapStart());
m_cbvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (int n = 0; n < DX::c_frameCount; n++)
{
D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
desc.BufferLocation = cbvGpuAddress;
desc.SizeInBytes = c_alignedConstantBufferSize;
d3dDevice->CreateConstantBufferView(&desc, cbvCpuHandle);
cbvGpuAddress += desc.SizeInBytes;
cbvCpuHandle.Offset(m_cbvDescriptorSize);
}
// Map the constant buffers.
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
DX::ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&m_mappedConstantBuffer)));
ZeroMemory(m_mappedConstantBuffer, DX::c_frameCount * c_alignedConstantBufferSize);
// We don't unmap this until the app closes. Keeping things mapped for the lifetime of the resource is okay.
이제 우리는 이 값을 CPU에서 읽으려 할 필요가 없다. 또한 app이 닫힐 때까지 이 연결을 해제하지 않는다.
constant buffer를 view와 연결하면 command list를 닫고, 안에 있는 명령들을 모두 실행한다.
그리고 vertex buffer와 index buffer에도 마찬가지로 view를 생성한다.
// Close the command list and execute it to begin the vertex/index buffer copy into the GPU's default heap.
DX::ThrowIfFailed(m_commandList->Close());
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// Create vertex/index buffer views.
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(VertexPositionColor);
m_vertexBufferView.SizeInBytes = sizeof(cubeVertices);
m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
m_indexBufferView.SizeInBytes = sizeof(cubeIndices);
m_indexBufferView.Format = DXGI_FORMAT_R16_UINT;
여기까지 작업이 완료 되었으면, GPU에서 연산이 종료 될 때까지 기다린다.
// Wait for the command list to finish executing; the vertex/index buffers need to be uploaded to the GPU before the upload resources go out of scope.
m_deviceResources->WaitForGpu();
작업이 모두 마쳐진 뒤에는 load가 완료 되었다는 의미에서 m_loadingComplete를 true로 바꿔준다.
DeviceDependentResources를 생성하고 나면 이번에는 CreateWindowSizeDependentResources() 차례이다.
이는 화면에 출력하는데 필요한 정보들을 app의 window 크기가 바뀔 때마다 새로 잡아주는 함수이다.
// Initializes view parameters when the window size changes.
void Sample3DSceneRenderer::CreateWindowSizeDependentResources()
{
Size outputSize = m_deviceResources->GetOutputSize();
float aspectRatio = outputSize.Width / outputSize.Height;
float fovAngleY = 70.0f * XM_PI / 180.0f;
D3D12_VIEWPORT viewport = m_deviceResources->GetScreenViewport();
m_scissorRect = { 0, 0, static_cast<LONG>(viewport.Width), static_cast<LONG>(viewport.Height)};
// This is a simple example of change that can be made when the app is in
// portrait or snapped view.
if (aspectRatio < 1.0f)
{
fovAngleY *= 2.0f;
}
// Note that the OrientationTransform3D matrix is post-multiplied here
// in order to correctly orient the scene to match the display orientation.
// This post-multiplication step is required for any draw calls that are
// made to the swap chain render target. For draw calls to other targets,
// this transform should not be applied.
// This sample makes use of a right-handed coordinate system using row-major matrices.
XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
fovAngleY,
aspectRatio,
0.01f,
100.0f
);
XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);
XMStoreFloat4x4(
&m_constantBufferData.projection,
XMMatrixTranspose(perspectiveMatrix * orientationMatrix)
);
// Eye is at (0,0.7,1.5), looking at point (0,-0.1,0) with the up-vector along the y-axis.
static const XMVECTORF32 eye = { 0.0f, 0.7f, 1.5f, 0.0f };
static const XMVECTORF32 at = { 0.0f, -0.1f, 0.0f, 0.0f };
static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
}
우선 생성자의 CreateDeviceDependentResources에서 mapping 했던 m_constantBuffer를 해제하고 있습니다.
또한 m_mappedConstantBuffer도 nullptr로 접근하지 못하도록 하였습니다.
이제 남은 함수 중 Render에 대해 먼저 보도록 합시다.
// Renders one frame using the vertex and pixel shaders.
bool Sample3DSceneRenderer::Render()
{
// Loading is asynchronous. Only draw geometry after it's loaded.
if (!m_loadingComplete)
{
return false;
}
DX::ThrowIfFailed(m_deviceResources->GetCommandAllocator()->Reset());
// The command list can be reset anytime after ExecuteCommandList() is called.
DX::ThrowIfFailed(m_commandList->Reset(m_deviceResources->GetCommandAllocator(), m_pipelineState.Get()));
PIXBeginEvent(m_commandList.Get(), 0, L"Draw the cube");
{
// Set the graphics root signature and descriptor heaps to be used by this frame.
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
ID3D12DescriptorHeap* ppHeaps[] = { m_cbvHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
// Bind the current frame's constant buffer to the pipeline.
CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle(m_cbvHeap->GetGPUDescriptorHandleForHeapStart(), m_deviceResources->GetCurrentFrameIndex(), m_cbvDescriptorSize);
m_commandList->SetGraphicsRootDescriptorTable(0, gpuHandle);
// Set the viewport and scissor rectangle.
D3D12_VIEWPORT viewport = m_deviceResources->GetScreenViewport();
m_commandList->RSSetViewports(1, &viewport);
m_commandList->RSSetScissorRects(1, &m_scissorRect);
// Indicate this resource will be in use as a render target.
CD3DX12_RESOURCE_BARRIER renderTargetResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
m_commandList->ResourceBarrier(1, &renderTargetResourceBarrier);
// Record drawing commands.
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = m_deviceResources->GetRenderTargetView();
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilView = m_deviceResources->GetDepthStencilView();
m_commandList->ClearRenderTargetView(renderTargetView, DirectX::Colors::CornflowerBlue, 0, nullptr);
m_commandList->ClearDepthStencilView(depthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
m_commandList->OMSetRenderTargets(1, &renderTargetView, false, &depthStencilView);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_commandList->IASetIndexBuffer(&m_indexBufferView);
m_commandList->DrawIndexedInstanced(36, 1, 0, 0, 0);
// Indicate that the render target will now be used to present when the command list is done executing.
CD3DX12_RESOURCE_BARRIER presentResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
m_commandList->ResourceBarrier(1, &presentResourceBarrier);
}
PIXEndEvent(m_commandList.Get());
DX::ThrowIfFailed(m_commandList->Close());
// Execute the command list.
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
return true;
}
우선 geometry가 loading 되었는지 m_loadingComplete로 확인합니다. Render연산은 한번만 호출되더라도 동작 할 때까지 호출 되기 때문에, loading 되어 있지 않으면 그냥 다음 frame이 될 때까지 아무 작업도 하지 않는다.
// Loading is asynchronous. Only draw geometry after it's loaded.
if (!m_loadingComplete)
{
return false;
}
Geometry가 모두 loading 되어 있다면 Command Allocator와 CommandList를 Reset한다.
만약 이 과정에서 오류가 발생 한다면, 새로운 Geometry는 그려지지 않고 함수 밖으로 에러가 던져진다.
DX::ThrowIfFailed(m_deviceResources->GetCommandAllocator()->Reset());
// The command list can be reset anytime after ExecuteCommandList() is called.
DX::ThrowIfFailed(m_commandList->Reset(m_deviceResources->GetCommandAllocator(), m_pipelineState.Get()));
이렇게 Reset 된 CommandList에 PIXEvent를 새로 넣어준다.
PIXBeginEvent(m_commandList.Get(), 0, L"Draw the cube");
{
// Set the graphics root signature and descriptor heaps to be used by this frame.
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
ID3D12DescriptorHeap* ppHeaps[] = { m_cbvHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
// Bind the current frame's constant buffer to the pipeline.
CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle(m_cbvHeap->GetGPUDescriptorHandleForHeapStart(), m_deviceResources->GetCurrentFrameIndex(), m_cbvDescriptorSize);
m_commandList->SetGraphicsRootDescriptorTable(0, gpuHandle);
// Set the viewport and scissor rectangle.
D3D12_VIEWPORT viewport = m_deviceResources->GetScreenViewport();
m_commandList->RSSetViewports(1, &viewport);
m_commandList->RSSetScissorRects(1, &m_scissorRect);
// Indicate this resource will be in use as a render target.
CD3DX12_RESOURCE_BARRIER renderTargetResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
m_commandList->ResourceBarrier(1, &renderTargetResourceBarrier);
// Record drawing commands.
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = m_deviceResources->GetRenderTargetView();
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilView = m_deviceResources->GetDepthStencilView();
m_commandList->ClearRenderTargetView(renderTargetView, DirectX::Colors::CornflowerBlue, 0, nullptr);
m_commandList->ClearDepthStencilView(depthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
m_commandList->OMSetRenderTargets(1, &renderTargetView, false, &depthStencilView);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_commandList->IASetIndexBuffer(&m_indexBufferView);
m_commandList->DrawIndexedInstanced(36, 1, 0, 0, 0);
// Indicate that the render target will now be used to present when the command list is done executing.
CD3DX12_RESOURCE_BARRIER presentResourceBarrier =
CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
m_commandList->ResourceBarrier(1, &presentResourceBarrier);
}
PIXEndEvent(m_commandList.Get());
먼저 Graphic root signature와 descriptor heap을 현재 frame에서 사용하도록 설정한다. 그리고 현재 frame의 constant buffer를 pipeline에 할당한다. 할당이 끝나면 viewport와 scissor rectangle을 설정한다. 여기서 나타낸 resource는 renter target에 사용된다.
이 작업이 모두 마치면 drawing command를 작성한다. 위에서 언급한 render target은 command list가 완전히 작동되기 전까지 사용되지 않는다.
// Called once per frame, rotates the cube and calculates the model and view matrices.
void Sample3DSceneRenderer::Update(DX::StepTimer const& timer)
{
if (m_loadingComplete)
{
if (!m_tracking)
{
// Rotate the cube a small amount.
m_angle += static_cast<float>(timer.GetElapsedSeconds()) * m_radiansPerSecond;
Rotate(m_angle);
}
// Update the constant buffer resource.
UINT8* destination = m_mappedConstantBuffer + (m_deviceResources->GetCurrentFrameIndex() * c_alignedConstantBufferSize);
memcpy(destination, &m_constantBufferData, sizeof(m_constantBufferData));
}
}
이 연산은 Geometry가 모두 load 되어 있어야 하며, tracking 상태가 아니면 Rotate 함수를 호출하여 회전을 시킨다.
여기서 호출하는 Rotate는 단순히 XMStoreFloat4x4를 이용한 연산이다.
// Rotate the 3D cube model a set amount of radians.
void Sample3DSceneRenderer::Rotate(float radians)
{
// Prepare to pass the updated model matrix to the shader.
XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
}
연산이 모두 끝나면 갱신된 정보를 constant buffer resource에 갱신한다.
마지막으로 소개 할 것은 SaveState이다.
생성자에서 LoadState가 호출 되었듯이, App이 종료될 때에는 SaveState가 호출된다.
// Saves the current state of the renderer.
void Sample3DSceneRenderer::SaveState()
{
auto state = ApplicationData::Current->LocalSettings->Values;
if (state->HasKey(AngleKey))
{
state->Remove(AngleKey);
}
if (state->HasKey(TrackingKey))
{
state->Remove(TrackingKey);
}
state->Insert(AngleKey, PropertyValue::CreateSingle(m_angle));
state->Insert(TrackingKey, PropertyValue::CreateBoolean(m_tracking));
}
내부에서 작동은 간단하다.
이전 Key가 있다면 키를 지우고, 새로운 Key를 저장한다.
이것으로 매우매우 간단하게 Sample3DSceneRenderer를 살펴보았다.
그리고 이걸 보고 느낀 점은, 따라서 못만든다는 것이다.
사용하는 라이브러리서부터 구조까지 아직 내 실력으로 짧은 시간 내에 이해하기 힘든 것들 뿐이다.
너무 잘 짜여져 있어서 제대로 이해하지 못하고 잘못 건드렸다가 더 큰 화를 부를 것 같기도 하고,
무엇보다 자신이 없다.
흥미가 있기도 하지만 현실적으로 좀 더 습득 속도를 요구하기 때문에
Windows Programming으로 DirectX를 먼저 공부하고, 이 C++/CX를 이용한 방법은 여유가 생길 때 하도록 보류한다.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
const int StringToInt(const string& str)
{
int ret = 0;
for (unsigned int i = 0; i < str.size(); ++i)
{
ret *= 10;
ret += str[i] - '0';
}
return ret;
}
const long long StringToList(const string& list)
{
long long ret = 0;
string temp;
vector<string> pParse;
stringstream ss(list);
while (getline(ss, temp, '+'))
{
pParse.push_back(temp);
}
for (const auto& iter : pParse)
{
ret += StringToInt(iter);
}
return ret;
}
int main()
{
long long ret = 0;
string input, temp;
vector<string> mParse;
vector<int> mParseNum;
cin >> input;
stringstream ss(input);
while (getline(ss, temp, '-'))
{
mParse.push_back(temp);
}
ret = StringToList(mParse[0]);
for (unsigned int i = 1; i < mParse.size(); ++i)
{
ret -= StringToList(mParse[i]);
}
cout << ret << endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int testcase = 0, M = 0, N = 0, x = 0, y = 0;
cin >> testcase;
while (testcase-- > 0)
{
int sum = 0;
cin >> M >> N >> x >> y;
while ((x != y) && (sum < (M * N)))
{
if (x > y)
{
y += N;
sum += N;
}
else
{
x += M;
}
}
if (sum >= (M * N))
{
cout << -1 << endl;
}
else
{
cout << x << endl;
}
}
}