ATOM-15352 Find a solution to modify a render pipeline when enable a feature gem (#960)

* Added atom_rpi_tools python module in Atom_RPI gem.
The tool includes functions to modify pass template data and some utility functions.
* Added pytest tests for atom_rpi_tools
This commit is contained in:
Qing Tao
2021-06-02 17:39:50 -07:00
committed by GitHub
parent f1dbeb584a
commit 312c704ba6
13 changed files with 857 additions and 0 deletions
+2
View File
@@ -10,3 +10,5 @@
#
add_subdirectory(Code)
add_subdirectory(Tools)
+23
View File
@@ -0,0 +1,23 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_pip_install_local_package_editable(${CMAKE_CURRENT_LIST_DIR} atom_rpi_tools)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_pytest(
NAME RPI::atom_rpi_tools_tests
PATH ${CMAKE_CURRENT_LIST_DIR}/atom_rpi_tools/tests/
TIMEOUT 30
)
endif()
endif()
+39
View File
@@ -0,0 +1,39 @@
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
INTRODUCTION
------------
atom_rpi_tools is a Python project that contains a collection of tools
developed by the Atom team. The project contains the following tools:
* Render pipeline merge tool:
A library to manipulate .pass asset files and help gems create scripts to update render pipeline
REQUIREMENTS
------------
* Python 3.7.5 (64-bit)
It is recommended that you completely remove any other versions of Python
installed on your system.
INSTALL
-----------
It is recommended to set up these these tools with Lumberyard's CMake build commands.
UNINSTALLATION
--------------
The preferred way to uninstall the project is:
(engine install root)/python/python -m pip uninstall atom_rpi_tools
+10
View File
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -0,0 +1,210 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import sys, os
import json
import shutil
class PassTemplate:
# This class provide necessary functions for insert pass requests and update connections
# which are common functions required for adding features.
# It doesn't include the remove/delete furnctions since that's not common case for merging render pipeline
def __init__(self, filePath: str):
self.initialized = False
self.file_path: str = filePath
#load the json file
json_data = open(filePath, "r")
self.file_data = json.load(json_data)
if 'ClassName' not in self.file_data or 'ClassData' not in self.file_data or self.file_data['ClassName']!='PassAsset' or 'PassTemplate' not in self.file_data['ClassData']:
raise KeyError('the json file is not a PassAsset file')
return
if 'PassRequests' in self.file_data['ClassData']['PassTemplate']:
self.passRequests = self.file_data['ClassData']['PassTemplate']['PassRequests']
if 'Slots' in self.file_data['ClassData']['PassTemplate']:
self.slots = self.file_data['ClassData']['PassTemplate']['Slots']
self.initialized = True
print('PassTemplate is loaded from ', filePath)
def find_pass(self, passName):
# return pass's index in PassRequests if a PassRequest with input passName exists
if not hasattr(self, 'passRequests'):
return -1
index = 0
for passRequest in self.passRequests:
if passRequest['Name'] == passName:
return index
index += 1
return -1
def get_pass_count(self):
if not hasattr(self, 'passRequests'):
return 0
return len(self.passRequests)
def __validate_pass_request_data(self, passRequest):
if ('Name' not in passRequest or 'TemplateName' not in passRequest):
raise KeyError('invalid pass request data')
def __ensure_pass_requests_key(self):
if not hasattr(self, 'passRequests'):
self.file_data['ClassData']['PassTemplate']['PassRequests'] = []
self.passRequests = self.file_data['ClassData']['PassTemplate']['PassRequests']
def __ensure_pass_slots_key(self):
if not hasattr(self, 'slots'):
self.file_data['ClassData']['PassTemplate']['Slots'] = []
self.slots = self.file_data['ClassData']['PassTemplate']['Slots']
def insert_pass_request(self, location, passRequest):
self.__validate_pass_request_data(passRequest)
if (self.find_pass(passRequest['Name']) >= 0):
raise ValueError('pass request ', passRequest['Name'], ' is already exist')
# insert a passRequest before the specified location
self.__ensure_pass_requests_key()
self.passRequests.insert(location, passRequest)
def replace_references_after(self, startPassRequest, oldPass, oldSlot, newPass, newSlot):
if not hasattr(self, 'passRequests'):
return 0
# from all pass requests after startPassRequest
# replace all attachment references which uses oldPass and oldSlot
# with newPass and newSlot
started = False
replaced_count = 0
for request in self.passRequests:
if started:
if ('Connections' in request):
for connection in request['Connections']:
if connection['AttachmentRef']['Pass'] == oldPass and connection['AttachmentRef']['Attachment'] == oldSlot:
connection['AttachmentRef']['Pass'] = newPass
connection['AttachmentRef']['Attachment'] = newSlot
replaced_count += 1
if request['Name'] == startPassRequest and not started:
started = True
return replaced_count
def replace_references_for(self, passRequest, oldPass, oldSlot, newPass, newSlot):
if not hasattr(self, 'passRequests'):
return 0
#replace pass reference for the specified passRequest
replaced_count = 0
for request in self.passRequests:
if request['Name'] == passRequest:
if ('Connections' in request):
for connection in request['Connections']:
if connection['AttachmentRef']['Pass'] == oldPass and connection['AttachmentRef']['Attachment'] == oldSlot:
connection['AttachmentRef']['Pass'] = newPass
connection['AttachmentRef']['Attachment'] = newSlot
replaced_count += 1
return replaced_count #return when the specified pass request is updated.
return replaced_count
def __validate_slot_data(self, slotData):
if ('Name' not in slotData or 'SlotType' not in slotData):
raise KeyError('invalid slot data')
def get_slot_count(self):
if not hasattr(self, 'slots'):
return 0
return len(self.slots)
def find_slot(self, slotName):
# return slot's index in Slots if a PassRequest with input passName exists
if not hasattr(self, 'slots'):
return -1
index = 0
for slot in self.slots:
if slot['Name'] == slotName:
return index
index += 1
return -1
def insert_slot(self, location, newSlotData):
# insert a new slot at specified location
self.__validate_slot_data(newSlotData)
# check if the slot already exist
if (self.find_slot(newSlotData['Name']) >= 0):
raise ValueError('Slot ', newSlotData['Name'], ' is already exist')
self.__ensure_pass_slots_key()
self.slots.insert(location, newSlotData)
def add_slot(self, newSlotData):
# append a new slot to slots
self.__validate_slot_data(newSlotData)
# check if the slot already exist
if (self.find_slot(newSlotData['Name']) >= 0):
raise ValueError('Slot ', newSlotData['Name'], ' is already exist')
self.__ensure_pass_slots_key()
self.slots.append(newSlotData)
def get_pass_request(self, passName):
if not hasattr(self, 'passRequests'):
return
# Get the pass request from PassRequests with matching pass name
for passRequest in self.passRequests:
if passRequest['Name'] == passName:
return passRequest
def save(self):
# backup the original file
backupFilePath = self.file_path +'.backup'
shutil.copyfile(self.file_path, backupFilePath)
# save and overwrite file
with open(self.file_path, 'w') as json_file:
json.dump(self.file_data, json_file, indent = 4)
print('File [', self.file_path, '] is updated. Old version is saved in [', backupFilePath, ']')
class PassRequest:
def __init__(self, passRequest: object):
self.pass_request = passRequest
if 'Connections' in passRequest:
self.connections = passRequest['Connections']
def __validate_connection(self, connection):
if ('LocalSlot' not in connection or 'AttachmentRef' not in connection):
raise KeyError('invalid connection data')
def __ensure_connections_key(self):
if not hasattr(self, 'connections'):
self.pass_request['Connections'] = []
self.connections = self.pass_request['Connections']
def get_connection_count(self):
if not hasattr(self, 'connections'):
return 0
return len(self.connections)
def find_connection(self, localSlotName):
if not hasattr(self, 'connections'):
return -1
index = 0
for connection in self.connections:
if connection['LocalSlot'] == localSlotName:
return index
index += 1
return -1
def add_connection(self, newConnection):
self.__validate_connection(newConnection)
if self.find_connection(newConnection['LocalSlot']) >= 0:
raise ValueError('connection ', newConnection['LocalSlot'], ' already exists')
self.__ensure_connections_key()
self.connections.append(newConnection)
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -0,0 +1,303 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Unit tests for pass_data.py
"""
import os
import pytest
import shutil
import json
from atom_rpi_tools.pass_data import PassTemplate
from atom_rpi_tools.pass_data import PassRequest
good_pass_requests_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_requests.json')
good_pass_slots_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_slots.json')
bad_test_data_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_test_bad.json')
@pytest.fixture
def pass_requests_template(tmpdir):
filename = 'pass_requests.json'
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
destFilePath = os.path.join(tmpdir, 'pass_requests.json')
shutil.copyfile(source_path, destFilePath)
return PassTemplate(destFilePath)
@pytest.fixture
def pass_slots_template(tmpdir):
filename = 'pass_slots.json'
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
destFilePath = os.path.join(tmpdir, 'pass_requests.json')
shutil.copyfile(source_path, destFilePath)
return PassTemplate(destFilePath)
@pytest.fixture
def new_pass_request():
pass_request = json.loads('{\"Name\": \"InsertPass\",\"TemplateName\": \"InsertPassTemplate\"}')
return pass_request
@pytest.fixture
def new_slot():
slot = json.loads('{\"Name\": \"NewSlot\",\"SlotType\": \"Input\"}')
return slot
@pytest.fixture
def new_connection():
connection = json.loads('{\"LocalSlot\": \"color\", \"AttachmentRef\": { \"Pass\": \"Parent\", \"Attachment\": \"DepthStencil\"}}')
return connection
def test_PassTemplate_Initialize_BadPassTemplateData_ExceptionThrown():
with pytest.raises(KeyError):
PassTemplate(bad_test_data_file)
def test_PassTemplate_FindPass_Success(pass_requests_template):
assert pass_requests_template.find_pass('OpaquePass') == 0
assert pass_requests_template.find_pass('ImGuiPass') == 4
assert pass_requests_template.find_pass('NotExistPass') == -1
def test_PassTemplate_InsertPassRequest_AtBegining_Success(pass_requests_template, new_pass_request):
template = pass_requests_template
pass_count = template.get_pass_count()
template.insert_pass_request(0, new_pass_request)
assert template.find_pass(new_pass_request['Name']) == 0
assert template.get_pass_count() == pass_count+1
# verify the change is saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.find_pass(new_pass_request['Name'])== 0
assert saved_tamplate.get_pass_count() == pass_count+1
def test_PassTemplate_InsertPassRequest_AtEnd_Success(pass_requests_template, new_pass_request):
template = pass_requests_template
pass_count = template.get_pass_count()
template.insert_pass_request(pass_count, new_pass_request)
assert template.find_pass(new_pass_request['Name']) == pass_count
assert template.get_pass_count() == pass_count+1
# verify the change is saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.find_pass(new_pass_request['Name']) == pass_count
assert saved_tamplate.get_pass_count() == pass_count+1
def test_PassTemplate_InsertPassRequest_WithDuplicatedName_ExceptionThrown(pass_requests_template, new_pass_request):
template = pass_requests_template
# insert new pass request
template.insert_pass_request(0, new_pass_request)
pass_count = template.get_pass_count()
# exception when insert the same pass again
with pytest.raises(ValueError):
template.insert_pass_request(2, new_pass_request)
# pass count doesn't change
assert template.get_pass_count() == pass_count
def test_PassTemplate_InsertPassRequest_WithBadData_ExceptionThrown(pass_requests_template):
template = pass_requests_template
pass_count = template.get_pass_count()
bad_pass_request = json.loads('{\"name\":\"value\"}')
with pytest.raises(KeyError):
template.insert_pass_request(2, bad_pass_request)
assert template.get_pass_count() == pass_count
def test_PassTemplate_InsertPassRequest_AtOutOfRange_AppendSuccess(pass_requests_template, new_pass_request):
template = pass_requests_template
pass_count = template.get_pass_count()
template.insert_pass_request(pass_count+2, new_pass_request)
assert template.find_pass(new_pass_request['Name']) == pass_count
assert template.get_pass_count() == pass_count+1
def test_PassTemplate_ReplaceReferencesAfter_Success(pass_requests_template):
# replace OpaquePass.DepthStencil with Parent.DepthStencil'
refPass = 'OpaquePass'
# there are 2 passes after OpaquePass which use OpaquePass.DepthStencil as attachment reference
assert pass_requests_template.replace_references_after(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 2
# after the previous replacement, there it no OpaquePass.DepthStencil reference
refPass = 'TransparentPass'
assert pass_requests_template.replace_references_after(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
# verify changes are saved
pass_requests_template.save()
saved_tamplate = PassTemplate(pass_requests_template.file_path)
assert saved_tamplate.replace_references_after('OpaquePass', 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
def test_PassTemplate_ReplaceReferencesFor_Success(pass_requests_template):
refPass = 'TransparentPass'
assert pass_requests_template.replace_references_for(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 1
refPass = '2DPass'
assert pass_requests_template.replace_references_for(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
# verify changes are saved
pass_requests_template.save()
saved_tamplate = PassTemplate(pass_requests_template.file_path)
# no reference of OpaquePass.DepthStencil in TransparentPass
assert saved_tamplate.replace_references_for('TransparentPass', 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
def test_PassTemplate_FindSlot_Success(pass_slots_template):
assert pass_slots_template.find_slot('Color') == -1
assert pass_slots_template.find_slot('DepthStencil') == 0
assert pass_slots_template.find_slot('ColorInputOutput') == 1
def test_PassTemplate_InsertSlot_AtBegining_Success(pass_slots_template, new_slot):
template = pass_slots_template
slot_count = template.get_slot_count()
depth_stencil_slot = template.find_slot('DepthStencil')
template.insert_slot(0, new_slot)
assert template.find_slot(new_slot['Name']) == 0
assert template.find_slot('DepthStencil') == depth_stencil_slot+1 # DepthStencil moved back by 1
assert template.get_slot_count() == slot_count+1
# verify the change is saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.find_slot(new_slot['Name']) == 0
assert saved_tamplate.get_slot_count() == slot_count+1
def test_PassTemplate_InsertSlot_AtEnd_Success(pass_slots_template, new_slot):
template = pass_slots_template
slot_count = template.get_slot_count()
depth_stencil_slot = template.find_slot('DepthStencil')
template.insert_slot(slot_count, new_slot)
assert template.find_slot(new_slot['Name']) == slot_count
assert template.find_slot('DepthStencil') == depth_stencil_slot
assert template.get_slot_count() == slot_count+1
# verify the change is saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.find_slot(new_slot['Name']) == slot_count
assert saved_tamplate.get_slot_count() == slot_count+1
def test_PassTemplate_AddSlot_GoodSlotData_Success(pass_slots_template, new_slot):
template = pass_slots_template
slot_count = template.get_slot_count()
depth_stencil_slot = template.find_slot('DepthStencil')
template.add_slot(new_slot)
assert template.find_slot(new_slot['Name']) == slot_count
assert template.find_slot('DepthStencil') == depth_stencil_slot
assert template.get_slot_count() == slot_count+1
# verify the change is saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.find_slot(new_slot['Name']) == slot_count
assert saved_tamplate.get_slot_count() == slot_count+1
def test_PassTemplate_InsertSlot_OutOfRange_AppendSuccess(pass_slots_template, new_slot):
template = pass_slots_template
slot_count = template.get_slot_count()
template.insert_slot(slot_count+3, new_slot)
assert template.find_slot(new_slot['Name']) == slot_count
assert template.get_slot_count() == slot_count+1
def test_PassTemplate_AddDuplicateSlot_ExceptionThrown(pass_slots_template, new_slot):
template = pass_slots_template
slot_count = template.get_slot_count()
template.add_slot(new_slot)
with pytest.raises(ValueError):
template.insert_slot(0, new_slot)
with pytest.raises(ValueError):
template.add_slot(new_slot)
def test_PassTemplate_InsertOrAddSlot_WithBadSlotData_ExceptionThrown(pass_slots_template):
template = pass_slots_template
slot_count = template.get_slot_count()
bad_slot = json.loads('{\"slot\": \"xxx\"}')
with pytest.raises(KeyError):
template.insert_slot(0, bad_slot)
with pytest.raises(KeyError):
template.add_slot(bad_slot)
def test_PassReqeuest_Initialize_WithExistPassReqeuestFromPassTemplate_Success(pass_requests_template):
template = pass_requests_template
request = PassRequest(template.get_pass_request('OpaquePass'))
connection_count = request.get_connection_count()
assert connection_count == 2
def test_PassTemplate_GetPassRequest_NotExist_ReturnNull(pass_requests_template):
assert not pass_requests_template.get_pass_request('NotExistPass')
def test_PassReqeuest_AddConnection_WithExistingConnections_Success(pass_requests_template, new_connection):
template = pass_requests_template
request = PassRequest(template.get_pass_request('OpaquePass'))
connection_count = request.get_connection_count()
request.add_connection(new_connection)
connection_count += 1
assert request.get_connection_count() == connection_count
# verify changes are saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
saved_request = PassRequest(saved_tamplate.get_pass_request('OpaquePass'))
assert saved_request.get_connection_count() == connection_count
def test_PassReqeuest_AddConnection_WithNoExistingConnections_Success(pass_requests_template, new_connection):
template = pass_requests_template
request = PassRequest(template.get_pass_request('ImGuiPass'))
assert request.get_connection_count() == 0
request.add_connection(new_connection)
assert request.get_connection_count() == 1
# verify changes are saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
saved_request = PassRequest(saved_tamplate.get_pass_request('ImGuiPass'))
assert saved_request.get_connection_count() == 1
def test_PassReqeuest_AddConnection_WithDuplicatedName_ExceptionThrown(pass_requests_template, new_connection):
template = pass_requests_template
request = PassRequest(template.get_pass_request('OpaquePass'))
request.add_connection(new_connection)
with pytest.raises(ValueError):
request.add_connection(new_connection)
def test_PassReqeuest_AddConnect_BadConnectionData_ExceptionThrown(pass_requests_template, new_connection):
template = pass_requests_template
request = PassRequest(template.get_pass_request('OpaquePass'))
bad_connection = json.loads('{\"xxx\": \"xxx\"}')
with pytest.raises(KeyError):
request.add_connection(bad_connection)
def test_PassTemplate_InsertSlot_ToEmptyList_Success(pass_requests_template, new_slot):
template = pass_requests_template
# test insert slot function to pass template which doesn't have any slots
slot_count = template.get_slot_count()
assert slot_count == 0
assert template.find_slot(new_slot['Name'])==-1
pass_requests_template.insert_slot(0, new_slot)
assert template.find_slot(new_slot['Name']) == 0
assert template.get_slot_count() == 1
# verify changes are saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.get_slot_count() == 1
def test_PassTempalte_InsertPassRequest_ToEmptyList_Success(pass_slots_template, new_pass_request):
template = pass_slots_template
# test insert pass function to pass template which doesn't have any pass requests
pass_count = template.get_pass_count()
assert pass_count == 0
template.insert_pass_request(0, new_pass_request)
assert template.find_pass(new_pass_request['Name']) == 0
assert template.get_pass_count() == 1
# verify changes are saved
template.save()
saved_tamplate = PassTemplate(template.file_path)
assert saved_tamplate.get_pass_count() == 1
def test_PassTemplate_Save_Success(pass_requests_template):
pass_requests_template.save()
saved_tamplate = PassTemplate(pass_requests_template.file_path)
assert os.path.exists(pass_requests_template.file_path)
assert os.path.exists(pass_requests_template.file_path +'.backup')
@@ -0,0 +1,53 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Unit tests for utils.py
"""
import pytest
import os
import atom_rpi_tools.utils as utils
def test_FindOrCopyFile_DestFileNotExist_CopySuccess(tmpdir):
# created dir and copied
filename = 'pass_requests.json'
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
dest_path = os.path.join(tmpdir, 'testdata/', 'pass_requests.json')
assert not os.path.exists(dest_path)
utils.find_or_copy_file(dest_path, source_path)
assert os.path.exists(dest_path)
source_size = os.path.getsize(source_path)
dest_size = os.path.getsize(dest_path)
assert source_size == dest_size
def test_FindOrCopyFile_DestFileAlreadyExists_Skip(tmpdir):
# copy %cur_dir%/testdata/pass_requests.json to tempdir/testdata/pass_requests.json
filename = 'pass_requests.json'
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
dest_path = os.path.join(tmpdir, 'testdata/', 'pass_requests.json')
utils.find_or_copy_file(dest_path, source_path)
# skip if dest_path already exists
assert os.path.exists(dest_path)
before_size = os.path.getsize(dest_path)
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', 'pass_slots.json')
before_source_size = os.path.getsize(source_path)
assert before_size != source_path
utils.find_or_copy_file(dest_path, source_path)
after_size = os.path.getsize(dest_path)
assert before_size == after_size
def test_FindOrCopyFile_SourceFileNotExists_ExceptionThrown(tmpdir):
# report error if source doesn't exist
bad_source_path = 'notexist.dat'
dest_path = os.path.join(tmpdir, 'notexist.dat')
with pytest.raises(ValueError):
utils.find_or_copy_file(dest_path, bad_source_path)
@@ -0,0 +1,116 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "PipelineTemplate",
"PassClass": "ParentPass",
"PassRequests": [
{
"Name": "OpaquePass",
"TemplateName": "OpaquePassTemplate",
"Connections": [
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "DepthStencil"
}
},
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "ColorInputOutput"
}
}
]
},
{
"Name": "TransparentPass",
"TemplateName": "TransparentPassTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "OpaquePass",
"Attachment": "DepthStencil"
}
},
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "OpaquePass",
"Attachment": "Color"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "transparent",
"DrawListSortType": "KeyThenReverseDepth",
"PipelineViewTag": "MainCamera",
"PassSrgAsset": {
"FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg"
}
}
},
{
"Name": "AuxGeomPass",
"TemplateName": "AuxGeomPassTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "OpaquePass",
"Attachment": "DepthStencil"
}
},
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "TransparentPass",
"Attachment": "ColorInputOutput"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "auxgeom",
"PipelineViewTag": "MainCamera"
}
},
{
"Name": "2DPass",
"TemplateName": "UIPassTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "TransparentPass",
"Attachment": "ColorInputOutput"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "2dpass",
"PipelineViewTag": "MainCamera"
}
},
{
"Name": "ImGuiPass",
"TemplateName": "ImGuiPassTemplate",
"PassData": {
"$type": "ImGuiPassData",
"IsDefaultImGui": true
}
}
]
}
}
}
@@ -0,0 +1,21 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "PipelineTemplate",
"PassClass": "ParentPass",
"Slots": [
{
"Name": "DepthStencil",
"SlotType": "InputOutput"
},
{
"Name": "ColorInputOutput",
"SlotType": "InputOutput"
}
]
}
}
}
@@ -0,0 +1,6 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassData": {
}
}
@@ -0,0 +1,31 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import os.path
from os import path
import shutil
import json
def find_or_copy_file(destFilePath, sourceFilePath):
if path.exists(destFilePath):
return
if not path.exists(sourceFilePath):
raise ValueError('find_or_copy_file: source file [', sourceFilePath, '] doesn\'t exist')
return
dstDir = path.dirname(destFilePath)
if not path.isdir(dstDir):
os.makedirs(dstDir)
shutil.copyfile(sourceFilePath, destFilePath)
def load_json_file(filePath):
file_stream = open(filePath, "r")
return json.load(file_stream)
+33
View File
@@ -0,0 +1,33 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import os
import platform
from setuptools import setup, find_packages
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
PYTHON_64 = platform.architecture()[0] == '64bit'
if __name__ == '__main__':
if not PYTHON_64:
raise RuntimeError("32-bit Python is not a supported platform.")
with open(os.path.join(PROJECT_ROOT, 'README.txt')) as f:
long_description = f.read()
setup(
name="atom_rpi_tools",
version="1.0.0",
description='Python interface to Atom RPI tools',
long_description=long_description,
packages=find_packages(exclude=['tests'])
)