blob: eed63439829511235472182a0d5e511d5ba399d8 [file] [log] [blame]
// VK tests
//
// Copyright (C) 2014 LunarG, Inc.
//
// 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 "vktestframework.h"
#include "vkrenderframework.h"
#include "SPIRV/GlslangToSpv.h"
#include "SPIRV/SPVRemapper.h"
#include <limits.h>
#include <math.h>
#include <wand/MagickWand.h>
#ifndef _WIN32
#include <xcb/xcb.h>
#endif
#include "vk_ext_khr_swapchain.h"
#include "vk_ext_khr_device_swapchain.h"
#if defined(PATH_MAX) && !defined(MAX_PATH)
#define MAX_PATH PATH_MAX
#endif
#ifdef _WIN32
#define ERR_EXIT(err_msg, err_class) \
do { \
MessageBox(NULL, err_msg, err_class, MB_OK); \
exit(1); \
} while (0)
#else // _WIN32
#define ERR_EXIT(err_msg, err_class) \
do { \
printf(err_msg); \
fflush(stdout); \
exit(1); \
} while (0)
#endif // _WIN32
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
{ \
m_fp##entrypoint = (PFN_vk##entrypoint) vkGetInstanceProcAddr(inst, "vk"#entrypoint); \
if (m_fp##entrypoint == NULL) { \
ERR_EXIT("vkGetInstanceProcAddr failed to find vk"#entrypoint, \
"vkGetInstanceProcAddr Failure"); \
} \
}
#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
{ \
m_fp##entrypoint = (PFN_vk##entrypoint) vkGetDeviceProcAddr(dev, "vk"#entrypoint); \
if (m_fp##entrypoint == NULL) { \
ERR_EXIT("vkGetDeviceProcAddr failed to find vk"#entrypoint, \
"vkGetDeviceProcAddr Failure"); \
} \
}
// Command-line options
enum TOptions {
EOptionNone = 0x000,
EOptionIntermediate = 0x001,
EOptionSuppressInfolog = 0x002,
EOptionMemoryLeakMode = 0x004,
EOptionRelaxedErrors = 0x008,
EOptionGiveWarnings = 0x010,
EOptionLinkProgram = 0x020,
EOptionMultiThreaded = 0x040,
EOptionDumpConfig = 0x080,
EOptionDumpReflection = 0x100,
EOptionSuppressWarnings = 0x200,
EOptionDumpVersions = 0x400,
EOptionSpv = 0x800,
EOptionDefaultDesktop = 0x1000,
};
typedef struct _SwapchainBuffers {
VkImage image;
VkCmdBuffer cmd;
VkImageView view;
} SwapchainBuffers;
class TestFrameworkVkPresent
{
public:
TestFrameworkVkPresent(vk_testing::Device &device);
void Run();
void InitPresentFramework(std::list<VkTestImageRecord> &imagesIn, VkInstance inst);
void CreateMyWindow();
void CreateSwapchain();
void SetImageLayout(VkImage image, VkImageAspectFlags aspectMask,
VkImageLayout old_image_layout, VkImageLayout new_image_layout);
void TearDown();
#ifdef _WIN32
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif
protected:
vk_testing::Device &m_device;
vk_testing::Queue &m_queue;
vk_testing::CmdPool m_cmdpool;
vk_testing::CmdBuffer m_cmdbuf;
private:
#ifdef _WIN32
HINSTANCE m_connection; // hInstance - Windows Instance
HWND m_window; // hWnd - window handle
#else
xcb_connection_t *m_connection;
xcb_screen_t *m_screen;
xcb_window_t m_window;
xcb_intern_atom_reply_t *m_atom_wm_delete_window;
VkPlatformHandleXcbKHR m_platform_handle_xcb;
#endif
std::list<VkTestImageRecord> m_images;
uint32_t m_present_queue_node_index;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_fpGetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetSurfacePropertiesKHR m_fpGetSurfacePropertiesKHR;
PFN_vkGetSurfaceFormatsKHR m_fpGetSurfaceFormatsKHR;
PFN_vkGetSurfacePresentModesKHR m_fpGetSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR m_fpCreateSwapchainKHR;
PFN_vkDestroySwapchainKHR m_fpDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR m_fpGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR m_fpAcquireNextImageKHR;
PFN_vkQueuePresentKHR m_fpQueuePresentKHR;
VkSurfaceDescriptionWindowKHR m_surface_description;
uint32_t m_swapchainImageCount;
VkSwapchainKHR m_swap_chain;
SwapchainBuffers *m_buffers;
VkFormat m_format;
VkColorSpaceKHR m_color_space;
uint32_t m_current_buffer;
bool m_quit;
bool m_pause;
int m_width;
int m_height;
std::list<VkTestImageRecord>::iterator m_display_image;
void Display();
#ifndef _WIN32
void HandleEvent(xcb_generic_event_t *event);
#endif
};
#ifndef _WIN32
#include <errno.h>
int fopen_s(
FILE** pFile,
const char* filename,
const char* mode
)
{
if (!pFile || !filename || !mode) {
return EINVAL;
}
FILE* f = fopen(filename, mode);
if (! f) {
if (errno != 0) {
return errno;
} else {
return ENOENT;
}
}
*pFile = f;
return 0;
}
#endif
// Set up environment for GLSL compiler
// Must be done once per process
void TestEnvironment::SetUp()
{
// Initialize GLSL to SPV compiler utility
glslang::InitializeProcess();
vk_testing::set_error_callback(test_error_callback);
}
void TestEnvironment::TearDown()
{
glslang::FinalizeProcess();
}
VkTestFramework::VkTestFramework() :
m_compile_options( 0 ),
m_num_shader_strings( 0 )
{
}
VkTestFramework::~VkTestFramework()
{
}
// Define all the static elements
bool VkTestFramework::m_show_images = false;
bool VkTestFramework::m_save_images = false;
bool VkTestFramework::m_compare_images = false;
bool VkTestFramework::m_use_glsl = false;
bool VkTestFramework::m_canonicalize_spv = false;
bool VkTestFramework::m_strip_spv = false;
bool VkTestFramework::m_do_everything_spv = false;
int VkTestFramework::m_width = 0;
int VkTestFramework::m_height = 0;
std::list<VkTestImageRecord> VkTestFramework::m_images;
std::list<VkTestImageRecord>::iterator VkTestFramework::m_display_image;
int m_display_image_idx = 0;
bool VkTestFramework::optionMatch(const char* option, char* optionLine)
{
if (strncmp(option, optionLine, strlen(option)) == 0)
return true;
else
return false;
}
void VkTestFramework::InitArgs(int *argc, char *argv[])
{
int i, n;
for (i=1, n=1; i< *argc; i++) {
if (optionMatch("--show-images", argv[i]))
m_show_images = true;
else if (optionMatch("--save-images", argv[i]))
m_save_images = true;
else if (optionMatch("--no-SPV", argv[i]))
m_use_glsl = true;
else if (optionMatch("--strip-SPV", argv[i]))
m_strip_spv = true;
else if (optionMatch("--canonicalize-SPV", argv[i]))
m_canonicalize_spv = true;
else if (optionMatch("--compare-images", argv[i]))
m_compare_images = true;
else if (optionMatch("--help", argv[i]) ||
optionMatch("-h", argv[i])) {
printf("\nOther options:\n");
printf("\t--show-images\n"
"\t\tDisplay test images in viewer after tests complete.\n");
printf("\t--save-images\n"
"\t\tSave tests images as ppm files in current working directory.\n"
"\t\tUsed to generate golden images for compare-images.\n");
printf("\t--compare-images\n"
"\t\tCompare test images to 'golden' image in golden folder.\n"
"\t\tAlso saves the generated test image in current working\n"
"\t\t\tdirectory but only if the image is different from the golden\n"
"\t\tSetting RENDERTEST_GOLDEN_DIR environment variable can specify\n"
"\t\t\tdifferent directory for golden images\n"
"\t\tSignal test failure if different.\n");
printf("\t--no-SPV\n"
"\t\tUse built-in GLSL compiler rather than SPV code path.\n");
printf("\t--strip-SPV\n"
"\t\tStrip SPIR-V debug information (line numbers, names, etc).\n");
printf("\t--canonicalize-SPV\n"
"\t\tRemap SPIR-V ids before submission to aid compression.\n");
exit(0);
} else {
printf("\nUnrecognized option: %s\n", argv[i]);
printf("\nUse --help or -h for option list.\n");
exit(0);
}
/*
* Since the above "consume" inputs, update argv
* so that it contains the trimmed list of args for glutInit
*/
argv[n] = argv[i];
n++;
}
}
void VkTestFramework::WritePPM( const char *basename, VkImageObj *image )
{
string filename;
VkResult err;
uint32_t x, y;
VkImageObj displayImage(image->device());
VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
displayImage.init(image->extent().width, image->extent().height, image->format(), VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT, VK_IMAGE_TILING_LINEAR, reqs);
displayImage.CopyImage(*image);
filename.append(basename);
filename.append(".ppm");
const VkImageSubresource sr = {
VK_IMAGE_ASPECT_COLOR, 0, 0
};
VkSubresourceLayout sr_layout;
err = vkGetImageSubresourceLayout(image->device()->device(), displayImage.image(), &sr, &sr_layout);
ASSERT_VK_SUCCESS( err );
char *ptr;
ptr = (char *) displayImage.MapMemory();
ptr += sr_layout.offset;
ofstream file (filename.c_str(), ios::binary);
ASSERT_TRUE(file.is_open()) << "Unable to open file: " << filename;
file << "P6\n";
file << displayImage.width() << " ";
file << displayImage.height() << "\n";
file << 255 << "\n";
for (y = 0; y < displayImage.height(); y++) {
const int *row = (const int *) ptr;
int swapped;
if (displayImage.format() == VK_FORMAT_B8G8R8A8_UNORM)
{
for (x = 0; x < displayImage.width(); x++) {
swapped = (*row & 0xff00ff00) | (*row & 0x000000ff) << 16 | (*row & 0x00ff0000) >> 16;
file.write((char *) &swapped, 3);
row++;
}
}
else if (displayImage.format() == VK_FORMAT_R8G8B8A8_UNORM)
{
for (x = 0; x < displayImage.width(); x++) {
file.write((char *) row, 3);
row++;
}
}
else {
printf("Unrecognized image format - will not write image files");
break;
}
ptr += sr_layout.rowPitch;
}
file.close();
displayImage.UnmapMemory();
}
void VkTestFramework::Compare(const char *basename, VkImageObj *image )
{
MagickWand *magick_wand_1;
MagickWand *magick_wand_2;
MagickWand *compare_wand;
MagickBooleanType status;
char testimage[256],golden[MAX_PATH+256],golddir[MAX_PATH] = "./golden";
double differenz;
if (getenv("RENDERTEST_GOLDEN_DIR"))
{
strcpy(golddir,getenv("RENDERTEST_GOLDEN_DIR"));
}
MagickWandGenesis();
magick_wand_1=NewMagickWand();
sprintf(testimage,"%s.ppm",basename);
status=MagickReadImage(magick_wand_1,testimage);
ASSERT_EQ(status, MagickTrue) << "Unable to open file: " << testimage;
MagickWandGenesis();
magick_wand_2=NewMagickWand();
sprintf(golden,"%s/%s.ppm",golddir,basename);
status=MagickReadImage(magick_wand_2,golden);
ASSERT_EQ(status, MagickTrue) << "Unable to open file: " << golden;
compare_wand=MagickCompareImages(magick_wand_1,magick_wand_2, MeanAbsoluteErrorMetric, &differenz);
if (differenz != 0.0)
{
char difference[256];
sprintf(difference,"%s-diff.ppm",basename);
status = MagickWriteImage(compare_wand, difference);
ASSERT_TRUE(differenz == 0.0) << "Image comparison failed - diff file written";
}
DestroyMagickWand(compare_wand);
DestroyMagickWand(magick_wand_1);
DestroyMagickWand(magick_wand_2);
MagickWandTerminus();
if (differenz == 0.0)
{
/*
* If test image and golden image match, we do not need to
* keep around the test image.
*/
remove(testimage);
}
}
void VkTestFramework::Show(const char *comment, VkImageObj *image)
{
VkResult err;
VkSubresourceLayout sr_layout;
char *ptr;
VkTestImageRecord record;
VkImageObj displayImage(image->device());
VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
displayImage.init(image->extent().width, image->extent().height, image->format(), VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT, VK_IMAGE_TILING_LINEAR, reqs);
displayImage.CopyImage(*image);
const VkImageSubresource sr = {
VK_IMAGE_ASPECT_COLOR, 0, 0
};
err = vkGetImageSubresourceLayout(displayImage.device()->device(), displayImage.image(), &sr, &sr_layout);
ASSERT_VK_SUCCESS( err );
ptr = (char *) displayImage.MapMemory();
ASSERT_VK_SUCCESS( err );
ptr += sr_layout.offset;
record.m_title.append(comment);
record.m_width = displayImage.width();
record.m_height = displayImage.height();
// TODO: Need to make this more robust to handle different image formats
record.m_data_size = displayImage.width() * displayImage.height() * 4;
record.m_data = malloc(record.m_data_size);
memcpy(record.m_data, ptr, record.m_data_size);
m_images.push_back(record);
m_display_image = --m_images.end();
displayImage.UnmapMemory();
}
void VkTestFramework::RecordImages(vector<VkImageObj *> images)
{
for (int32_t i = 0; i < images.size(); i++) {
RecordImage(images[i]);
}
}
void VkTestFramework::RecordImage(VkImageObj * image)
{
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ostringstream filestream;
string filename;
m_width = 40;
if (strcmp(test_info->name(), m_testName.c_str())) {
filestream << test_info->name();
m_testName.assign(test_info->name());
m_frameNum = 2;
filename = filestream.str();
}
else {
filestream << test_info->name() << "-" << m_frameNum;
m_frameNum++;
filename = filestream.str();
}
// ToDo - scrub string for bad characters
if (m_save_images || m_compare_images) {
WritePPM(filename.c_str(), image);
if (m_compare_images) {
Compare(filename.c_str(), image);
}
}
if (m_show_images) {
Show(test_info->name(), image);
}
}
TestFrameworkVkPresent::TestFrameworkVkPresent(vk_testing::Device &device) :
m_device(device),
m_queue(*m_device.graphics_queues()[0]),
m_cmdpool(m_device, vk_testing::CmdPool::create_info(m_device.graphics_queue_node_index_)),
m_cmdbuf(m_device, vk_testing::CmdBuffer::create_info(m_cmdpool.handle()))
{
m_quit = false;
m_pause = false;
m_width = 0;
m_height = 0;
}
void TestFrameworkVkPresent::Display()
{
VkResult U_ASSERT_ONLY err;
vk_testing::Buffer buf;
void *dest_ptr;
VkSemaphore presentCompleteSemaphore;
VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = {};
presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
presentCompleteSemaphoreCreateInfo.pNext = NULL;
presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
err = vkCreateSemaphore(m_device.handle(),
&presentCompleteSemaphoreCreateInfo,
&presentCompleteSemaphore);
assert(!err);
// Get the index of the next available swapchain image:
err = m_fpAcquireNextImageKHR(m_device.handle(), m_swap_chain,
UINT64_MAX,
presentCompleteSemaphore,
&m_current_buffer);
// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
// return codes
assert(!err);
// Wait for the present complete semaphore to be signaled to ensure
// that the image won't be rendered to until the presentation
// engine has fully released ownership to the application, and it is
// okay to render to the image.
vkQueueWaitSemaphore(m_queue.handle(), presentCompleteSemaphore);
vkDestroySemaphore(m_device.handle(), presentCompleteSemaphore);
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
buf.init_as_src(m_device, (VkDeviceSize)m_display_image->m_data_size, flags);
dest_ptr = buf.memory().map();
memcpy(dest_ptr, m_display_image->m_data, m_display_image->m_data_size);
buf.memory().unmap();
m_cmdbuf.begin();
VkImageMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
memoryBarrier.pNext = NULL;
memoryBarrier.outputMask = VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT;
memoryBarrier.inputMask = 0;
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR;
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL;
memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
memoryBarrier.destQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
memoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
memoryBarrier.subresourceRange.baseMipLevel = 0;
memoryBarrier.subresourceRange.mipLevels = 1;
memoryBarrier.subresourceRange.baseArrayLayer = 0;
memoryBarrier.subresourceRange.arraySize = 1;
memoryBarrier.image = m_buffers[m_current_buffer].image;
VkImageMemoryBarrier *pmemory_barrier = &memoryBarrier;
vkCmdPipelineBarrier(m_cmdbuf.handle(), VK_PIPELINE_STAGE_ALL_GPU_COMMANDS, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_FALSE, 1, (const void * const*)&pmemory_barrier);
VkBufferImageCopy region = {};
region.imageExtent.height = m_display_image->m_height;
region.imageExtent.width = m_display_image->m_width;
region.imageExtent.depth = 1;
vkCmdCopyBufferToImage(m_cmdbuf.handle(),
buf.handle(),
m_buffers[m_current_buffer].image, VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL,
1, &region);
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL;
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR;
vkCmdPipelineBarrier(m_cmdbuf.handle(), VK_PIPELINE_STAGE_ALL_GPU_COMMANDS, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_FALSE, 1, (const void * const*)&pmemory_barrier);
m_cmdbuf.end();
VkCmdBuffer cmdBufs[1];
cmdBufs[0] = m_cmdbuf.handle();
VkFence nullFence = { VK_NULL_HANDLE };
vkQueueSubmit(m_queue.handle(), 1, cmdBufs, nullFence);
m_queue.wait();
VkPresentInfoKHR present = {};
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present.pNext = NULL;
present.swapchainCount = 1;
present.swapchains = & m_swap_chain;
present.imageIndices = &m_current_buffer;
#ifndef _WIN32
xcb_change_property (m_connection,
XCB_PROP_MODE_REPLACE,
m_window,
XCB_ATOM_WM_NAME,
XCB_ATOM_STRING,
8,
m_display_image->m_title.size(),
m_display_image->m_title.c_str());
#endif
err = m_fpQueuePresentKHR(m_queue.handle(), &present);
assert(!err);
m_queue.wait();
m_current_buffer = (m_current_buffer + 1) % 2;
}
#ifdef _WIN32
# define PREVIOUSLY_DOWN 1<<29
// MS-Windows event handling function:
LRESULT CALLBACK TestFrameworkVkPresent::WndProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_PAINT:
{
TestFrameworkVkPresent* me = reinterpret_cast<TestFrameworkVkPresent*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (me) {
SetWindowText(hWnd, me->m_display_image->m_title.c_str());
me->Display();
}
}
break;
case WM_KEYDOWN:
{
if (lParam & (PREVIOUSLY_DOWN)){
break;
}
// To be able to be a CALLBACK, WndProc had to be static, so it doesn't get a this pointer. When we created
// the window, we put the this pointer into the window's user data so we could get it back now
TestFrameworkVkPresent* me = reinterpret_cast<TestFrameworkVkPresent*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
switch (wParam)
{
case VK_ESCAPE: me->m_quit = true;
break;
case VK_LEFT: // left arrow key
if (me->m_display_image == me->m_images.begin()) {
me->m_display_image = --me->m_images.end();
}
else {
--me->m_display_image;
}
break;
case VK_RIGHT: // right arrow key
++me->m_display_image;
if (me->m_display_image == me->m_images.end()) {
me->m_display_image = me->m_images.begin();
}
break;
default:
break;
}
SetWindowText(hWnd, me->m_display_image->m_title.c_str());
me->Display();
}
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
void TestFrameworkVkPresent::Run()
{
MSG msg; // message
/* main message loop*/
while(! m_quit) {
GetMessage(&msg, m_window, 0, 0);
if (msg.message == WM_QUIT) {
m_quit = true; //if found, quit app
} else {
/* Translate and dispatch to event queue*/
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
#else
void TestFrameworkVkPresent::HandleEvent(xcb_generic_event_t *event)
{
uint8_t event_code = event->response_type & 0x7f;
switch (event_code) {
case XCB_EXPOSE:
Display(); // TODO: handle resize
break;
case XCB_CLIENT_MESSAGE:
if((*(xcb_client_message_event_t*)event).data.data32[0] ==
(m_atom_wm_delete_window)->atom) {
m_quit = true;
}
break;
case XCB_KEY_RELEASE:
{
const xcb_key_release_event_t *key =
(const xcb_key_release_event_t *) event;
switch (key->detail) {
case 0x9: // Escape
m_quit = true;
break;
case 0x71: // left arrow key
if (m_display_image == m_images.begin()) {
m_display_image = --m_images.end();
} else {
--m_display_image;
}
break;
case 0x72: // right arrow key
++m_display_image;
if (m_display_image == m_images.end()) {
m_display_image = m_images.begin();
}
break;
case 0x41:
m_pause = !m_pause;
break;
}
Display();
}
break;
default:
break;
}
}
void TestFrameworkVkPresent::Run()
{
xcb_flush(m_connection);
while (! m_quit) {
xcb_generic_event_t *event;
if (m_pause) {
event = xcb_wait_for_event(m_connection);
} else {
event = xcb_poll_for_event(m_connection);
}
if (event) {
HandleEvent(event);
free(event);
}
}
}
#endif // _WIN32
void TestFrameworkVkPresent::CreateSwapchain()
{
VkResult U_ASSERT_ONLY err;
m_display_image = m_images.begin();
m_current_buffer = 0;
// Construct the WSI surface description:
m_surface_description.sType = VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_KHR;
m_surface_description.pNext = NULL;
#ifdef _WIN32
m_surface_description.platform = VK_PLATFORM_WIN32_KHR;
m_surface_description.pPlatformHandle = m_connection;
m_surface_description.pPlatformWindow = m_window;
#else // _WIN32
m_platform_handle_xcb.connection = m_connection;
m_platform_handle_xcb.root = m_screen->root;
m_surface_description.platform = VK_PLATFORM_XCB_KHR;
m_surface_description.pPlatformHandle = &m_platform_handle_xcb;
m_surface_description.pPlatformWindow = &m_window;
#endif // _WIN32
// Iterate over each queue to learn whether it supports presenting to WSI:
VkBool32 supportsPresent;
m_present_queue_node_index = UINT32_MAX;
std::vector<vk_testing::Queue *> queues = m_device.graphics_queues();
for (int i=0; i < queues.size(); i++)
{
int family_index = queues[i]->get_family_index();
m_fpGetPhysicalDeviceSurfaceSupportKHR(m_device.phy().handle(),
family_index,
(VkSurfaceDescriptionKHR *) &m_surface_description,
&supportsPresent);
if (supportsPresent) {
m_present_queue_node_index = family_index;
}
}
assert(m_present_queue_node_index != UINT32_MAX);
// Get the list of VkFormat's that are supported:
uint32_t formatCount;
err = m_fpGetSurfaceFormatsKHR(m_device.handle(),
(VkSurfaceDescriptionKHR *) &m_surface_description,
&formatCount, NULL);
assert(!err);
VkSurfaceFormatKHR *surfFormats =
(VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
err = m_fpGetSurfaceFormatsKHR(m_device.handle(),
(VkSurfaceDescriptionKHR *) &m_surface_description,
&formatCount, surfFormats);
assert(!err);
// If the format list includes just one entry of VK_FORMAT_UNDEFINED,
// the surface has no preferred format. Otherwise, at least one
// supported format will be returned.
if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)
{
m_format = VK_FORMAT_B8G8R8A8_UNORM;
}
else
{
assert(formatCount >= 1);
m_format = surfFormats[0].format;
}
m_color_space = surfFormats[0].colorSpace;
// Check the surface proprties and formats
VkSurfacePropertiesKHR surfProperties;
err = m_fpGetSurfacePropertiesKHR(m_device.handle(),
(const VkSurfaceDescriptionKHR *)&m_surface_description,
&surfProperties);
assert(!err);
uint32_t presentModeCount;
err = m_fpGetSurfacePresentModesKHR(m_device.handle(),
(const VkSurfaceDescriptionKHR *)&m_surface_description,
&presentModeCount, NULL);
assert(!err);
VkPresentModeKHR *presentModes =
(VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
assert(presentModes);
err = m_fpGetSurfacePresentModesKHR(m_device.handle(),
(const VkSurfaceDescriptionKHR *)&m_surface_description,
&presentModeCount, presentModes);
assert(!err);
VkExtent2D swapchainExtent;
// width and height are either both -1, or both not -1.
if (surfProperties.currentExtent.width == -1)
{
// If the surface size is undefined, the size is set to
// the size of the images requested.
swapchainExtent.width = m_width;
swapchainExtent.height = m_height;
}
else
{
// If the surface size is defined, the swap chain size must match
swapchainExtent = surfProperties.currentExtent;
}
// If mailbox mode is available, use it, as is the lowest-latency non-
// tearing mode. If not, try IMMEDIATE which will usually be available,
// and is fastest (though it tears). If not, fall back to FIFO which is
// always available.
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
for (size_t i = 0; i < presentModeCount; i++) {
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
}
// Determine the number of VkImage's to use in the swap chain (we desire to
// own only 1 image at a time, besides the images being displayed and
// queued for display):
uint32_t desiredNumberOfSwapchainImages = surfProperties.minImageCount + 1;
if ((surfProperties.maxImageCount > 0) &&
(desiredNumberOfSwapchainImages > surfProperties.maxImageCount))
{
// Application must settle for fewer images than desired:
desiredNumberOfSwapchainImages = surfProperties.maxImageCount;
}
VkSurfaceTransformKHR preTransform;
if (surfProperties.supportedTransforms & VK_SURFACE_TRANSFORM_NONE_BIT_KHR) {
preTransform = VK_SURFACE_TRANSFORM_NONE_KHR;
} else {
preTransform = surfProperties.currentTransform;
}
// We want to blit to the swap chain, ensure the driver supports it. Color is always supported, per WSI spec.
assert((surfProperties.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT) != 0);
VkSwapchainCreateInfoKHR swap_chain = {};
swap_chain.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swap_chain.pNext = NULL;
swap_chain.pSurfaceDescription = (const VkSurfaceDescriptionKHR *)&m_surface_description;
swap_chain.minImageCount = desiredNumberOfSwapchainImages;
swap_chain.imageFormat = m_format;
swap_chain.imageColorSpace = m_color_space;
swap_chain.imageExtent.width = swapchainExtent.width;
swap_chain.imageExtent.height = swapchainExtent.height;
swap_chain.imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swap_chain.preTransform = preTransform;
swap_chain.imageArraySize = 1;
swap_chain.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
swap_chain.queueFamilyCount = 0;
swap_chain.pQueueFamilyIndices = NULL;
swap_chain.presentMode = swapchainPresentMode;
swap_chain.oldSwapchain.handle = 0;
swap_chain.clipped = true;
uint32_t i;
err = m_fpCreateSwapchainKHR(m_device.handle(), &swap_chain, &m_swap_chain);
assert(!err);
err = m_fpGetSwapchainImagesKHR(m_device.handle(), m_swap_chain,
&m_swapchainImageCount, NULL);
assert(!err);
VkImage* swapchainImages = (VkImage*)malloc(m_swapchainImageCount * sizeof(VkImage));
assert(swapchainImages);
err = m_fpGetSwapchainImagesKHR(m_device.handle(), m_swap_chain,
&m_swapchainImageCount, swapchainImages);
assert(!err);
m_buffers = (SwapchainBuffers*)malloc(sizeof(SwapchainBuffers)*m_swapchainImageCount);
assert(m_buffers);
for (i = 0; i < m_swapchainImageCount; i++) {
VkImageViewCreateInfo color_image_view = {};
color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
color_image_view.pNext = NULL;
color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
color_image_view.format = m_format;
color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
color_image_view.subresourceRange.baseMipLevel = 0;
color_image_view.subresourceRange.mipLevels = 1;
color_image_view.subresourceRange.baseArrayLayer = 0;
color_image_view.subresourceRange.arraySize = 1;
m_buffers[i].image = swapchainImages[i];
color_image_view.image = m_buffers[i].image;
err = vkCreateImageView(m_device.handle(),
&color_image_view, &m_buffers[i].view);
assert(!err);
/* Set image layout to PRESENT_SOURCE_KHR so that before the copy, it can be set to */
/* TRANSFER_DESTINATION_OPTIMAL */
SetImageLayout(m_buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR);
}
}
void TestFrameworkVkPresent::SetImageLayout(VkImage image, VkImageAspectFlags aspectMask,
VkImageLayout old_image_layout, VkImageLayout new_image_layout)
{
VkResult U_ASSERT_ONLY err;
VkCmdBufferBeginInfo cmd_buf_info = {};
cmd_buf_info.sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO;
cmd_buf_info.pNext = NULL;
cmd_buf_info.flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT;
cmd_buf_info.renderPass = { VK_NULL_HANDLE };
cmd_buf_info.subpass = 0;
cmd_buf_info.framebuffer = { VK_NULL_HANDLE };
err = vkBeginCommandBuffer(m_cmdbuf.handle(), &cmd_buf_info);
assert(!err);
VkImageMemoryBarrier image_memory_barrier = {};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = NULL;
image_memory_barrier.outputMask = 0;
image_memory_barrier.inputMask = 0;
image_memory_barrier.oldLayout = old_image_layout;
image_memory_barrier.newLayout = new_image_layout;
image_memory_barrier.image = image;
image_memory_barrier.subresourceRange.aspectMask = aspectMask;
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.mipLevels = 1;
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
image_memory_barrier.subresourceRange.arraySize = 1;
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL) {
/* Make sure anything that was copying from this image has completed */
image_memory_barrier.inputMask = VK_MEMORY_INPUT_TRANSFER_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
/* Make sure any Copy or CPU writes to image are flushed */
image_memory_barrier.outputMask = VK_MEMORY_OUTPUT_HOST_WRITE_BIT | VK_MEMORY_OUTPUT_TRANSFER_BIT;
}
VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkCmdPipelineBarrier(m_cmdbuf.handle(), src_stages, dest_stages, false, 1, (const void * const*)&pmemory_barrier);
err = vkEndCommandBuffer(m_cmdbuf.handle());
assert(!err);
const VkCmdBuffer cmd_bufs[] = { m_cmdbuf.handle() };
VkFence nullFence = { VK_NULL_HANDLE };
err = vkQueueSubmit(m_queue.handle(), 1, cmd_bufs, nullFence);
assert(!err);
err = vkQueueWaitIdle(m_queue.handle());
assert(!err);
}
void TestFrameworkVkPresent::InitPresentFramework(std::list<VkTestImageRecord> &imagesIn, VkInstance inst)
{
GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceSupportKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), GetSurfacePropertiesKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), GetSurfaceFormatsKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), GetSurfacePresentModesKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), CreateSwapchainKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), CreateSwapchainKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), DestroySwapchainKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), GetSwapchainImagesKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), AcquireNextImageKHR);
GET_DEVICE_PROC_ADDR(m_device.handle(), QueuePresentKHR);
m_images = imagesIn;
}
#ifdef _WIN32
void TestFrameworkVkPresent::CreateMyWindow()
{
WNDCLASSEX win_class;
// const ::testing::TestInfo* const test_info =
// ::testing::UnitTest::GetInstance()->current_test_info();
m_connection = GetModuleHandle(NULL);
for (std::list<VkTestImageRecord>::const_iterator it = m_images.begin();
it != m_images.end(); it++) {
if (m_width < it->m_width)
m_width = it->m_width;
if (m_height < it->m_height)
m_height = it->m_height;
}
// Initialize the window class structure:
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = (WNDPROC) &TestFrameworkVkPresent::WndProc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = 0;
win_class.hInstance = m_connection; // hInstance
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = "Test";
win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
// Register window class:
if (!RegisterClassEx(&win_class)) {
// It didn't work, so try to give a useful error:
printf("Unexpected error trying to start the application!\n");
fflush(stdout);
exit(1);
}
// Create window with the registered class:
RECT wr = { 0, 0, m_width, m_height };
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
m_window = CreateWindowEx(0,
"Test", // class name
"Test", // app name
WS_OVERLAPPEDWINDOW | // window style
WS_VISIBLE |
WS_SYSMENU,
100,100, // x/y coords
wr.right - wr.left, // width
wr.bottom - wr.top, // height
NULL, // handle to parent
NULL, // handle to menu
m_connection, // hInstance
NULL); // no extra parameters
if (!m_window) {
// It didn't work, so try to give a useful error:
DWORD error = GetLastError();
char message[120];
sprintf(message, "Cannot create a window in which to draw!\n GetLastError = %d", error);
MessageBox(NULL, message, "Error", MB_OK);
exit(1);
}
// Put our this pointer into the window's user data so our WndProc can use it when it starts.
SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR) this);
}
#else
void TestFrameworkVkPresent::CreateMyWindow()
{
const xcb_setup_t *setup;
xcb_screen_iterator_t iter;
int scr;
uint32_t value_mask, value_list[32];
m_connection = xcb_connect(NULL, &scr);
setup = xcb_get_setup(m_connection);
iter = xcb_setup_roots_iterator(setup);
while (scr-- > 0)
xcb_screen_next(&iter);
m_screen = iter.data;
for (std::list<VkTestImageRecord>::const_iterator it = m_images.begin();
it != m_images.end(); it++) {
if (m_width < it->m_width)
m_width = it->m_width;
if (m_height < it->m_height)
m_height = it->m_height;
}
m_window = xcb_generate_id(m_connection);
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
value_list[0] = m_screen->black_pixel;
value_list[1] = XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY;
xcb_create_window(m_connection,
XCB_COPY_FROM_PARENT,
m_window, m_screen->root,
0, 0, m_width, m_height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
m_screen->root_visual,
value_mask, value_list);
/* Magic code that will send notification when window is destroyed */
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(m_connection, 1, 12,
"WM_PROTOCOLS");
xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(m_connection, cookie, 0);
xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(m_connection, 0, 16, "WM_DELETE_WINDOW");
m_atom_wm_delete_window = xcb_intern_atom_reply(m_connection, cookie2, 0);
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE,
m_window, (*reply).atom, 4, 32, 1,
&(*m_atom_wm_delete_window).atom);
free(reply);
xcb_map_window(m_connection, m_window);
}
#endif
void TestFrameworkVkPresent::TearDown()
{
m_fpDestroySwapchainKHR(m_device.handle(), m_swap_chain);
for (uint32_t i = 0; i < m_swapchainImageCount; i++) {
vkDestroyImageView(m_device.handle(), m_buffers[i].view);
}
#ifndef _WIN32
xcb_destroy_window(m_connection, m_window);
xcb_disconnect(m_connection);
#endif
}
void VkTestFramework::Finish()
{
if (m_images.size() == 0) return;
vk_testing::Environment env;
env.SetUp();
{
TestFrameworkVkPresent vkPresent(env.default_device());
vkPresent.InitPresentFramework(m_images, env.get_instance());
vkPresent.CreateMyWindow();
vkPresent.CreateSwapchain();
vkPresent.Run();
vkPresent.TearDown();
}
env.TearDown();
}
//
// These are the default resources for TBuiltInResources, used for both
// - parsing this string for the case where the user didn't supply one
// - dumping out a template for user construction of a config file
//
static const char* DefaultConfig =
"MaxLights 32\n"
"MaxClipPlanes 6\n"
"MaxTextureUnits 32\n"
"MaxTextureCoords 32\n"
"MaxVertexAttribs 64\n"
"MaxVertexUniformComponents 4096\n"
"MaxVaryingFloats 64\n"
"MaxVertexTextureImageUnits 32\n"
"MaxCombinedTextureImageUnits 80\n"
"MaxTextureImageUnits 32\n"
"MaxFragmentUniformComponents 4096\n"
"MaxDrawBuffers 32\n"
"MaxVertexUniformVectors 128\n"
"MaxVaryingVectors 8\n"
"MaxFragmentUniformVectors 16\n"
"MaxVertexOutputVectors 16\n"
"MaxFragmentInputVectors 15\n"
"MinProgramTexelOffset -8\n"
"MaxProgramTexelOffset 7\n"
"MaxClipDistances 8\n"
"MaxComputeWorkGroupCountX 65535\n"
"MaxComputeWorkGroupCountY 65535\n"
"MaxComputeWorkGroupCountZ 65535\n"
"MaxComputeWorkGroupSizeX 1024\n"
"MaxComputeWorkGroupSizeY 1024\n"
"MaxComputeWorkGroupSizeZ 64\n"
"MaxComputeUniformComponents 1024\n"
"MaxComputeTextureImageUnits 16\n"
"MaxComputeImageUniforms 8\n"
"MaxComputeAtomicCounters 8\n"
"MaxComputeAtomicCounterBuffers 1\n"
"MaxVaryingComponents 60\n"
"MaxVertexOutputComponents 64\n"
"MaxGeometryInputComponents 64\n"
"MaxGeometryOutputComponents 128\n"
"MaxFragmentInputComponents 128\n"
"MaxImageUnits 8\n"
"MaxCombinedImageUnitsAndFragmentOutputs 8\n"
"MaxCombinedShaderOutputResources 8\n"
"MaxImageSamples 0\n"
"MaxVertexImageUniforms 0\n"
"MaxTessControlImageUniforms 0\n"
"MaxTessEvaluationImageUniforms 0\n"
"MaxGeometryImageUniforms 0\n"
"MaxFragmentImageUniforms 8\n"
"MaxCombinedImageUniforms 8\n"
"MaxGeometryTextureImageUnits 16\n"
"MaxGeometryOutputVertices 256\n"
"MaxGeometryTotalOutputComponents 1024\n"
"MaxGeometryUniformComponents 1024\n"
"MaxGeometryVaryingComponents 64\n"
"MaxTessControlInputComponents 128\n"
"MaxTessControlOutputComponents 128\n"
"MaxTessControlTextureImageUnits 16\n"
"MaxTessControlUniformComponents 1024\n"
"MaxTessControlTotalOutputComponents 4096\n"
"MaxTessEvaluationInputComponents 128\n"
"MaxTessEvaluationOutputComponents 128\n"
"MaxTessEvaluationTextureImageUnits 16\n"
"MaxTessEvaluationUniformComponents 1024\n"
"MaxTessPatchComponents 120\n"
"MaxPatchVertices 32\n"
"MaxTessGenLevel 64\n"
"MaxViewports 16\n"
"MaxVertexAtomicCounters 0\n"
"MaxTessControlAtomicCounters 0\n"
"MaxTessEvaluationAtomicCounters 0\n"
"MaxGeometryAtomicCounters 0\n"
"MaxFragmentAtomicCounters 8\n"
"MaxCombinedAtomicCounters 8\n"
"MaxAtomicCounterBindings 1\n"
"MaxVertexAtomicCounterBuffers 0\n"
"MaxTessControlAtomicCounterBuffers 0\n"
"MaxTessEvaluationAtomicCounterBuffers 0\n"
"MaxGeometryAtomicCounterBuffers 0\n"
"MaxFragmentAtomicCounterBuffers 1\n"
"MaxCombinedAtomicCounterBuffers 1\n"
"MaxAtomicCounterBufferSize 16384\n"
"MaxTransformFeedbackBuffers 4\n"
"MaxTransformFeedbackInterleavedComponents 64\n"
"MaxCullDistances 8\n"
"MaxCombinedClipAndCullDistances 8\n"
"MaxSamples 4\n"
"nonInductiveForLoops 1\n"
"whileLoops 1\n"
"doWhileLoops 1\n"
"generalUniformIndexing 1\n"
"generalAttributeMatrixVectorIndexing 1\n"
"generalVaryingIndexing 1\n"
"generalSamplerIndexing 1\n"
"generalVariableIndexing 1\n"
"generalConstantMatrixVectorIndexing 1\n"
;
//
// *.conf => this is a config file that can set limits/resources
//
bool VkTestFramework::SetConfigFile(const std::string& name)
{
if (name.size() < 5)
return false;
if (name.compare(name.size() - 5, 5, ".conf") == 0) {
ConfigFile = name;
return true;
}
return false;
}
//
// Parse either a .conf file provided by the user or the default string above.
//
void VkTestFramework::ProcessConfigFile()
{
char** configStrings = 0;
char* config = 0;
if (ConfigFile.size() > 0) {
configStrings = ReadFileData(ConfigFile.c_str());
if (configStrings)
config = *configStrings;
else {
printf("Error opening configuration file; will instead use the default configuration\n");
}
}
if (config == 0) {
config = (char *) alloca(strlen(DefaultConfig) + 1);
strcpy(config, DefaultConfig);
}
const char* delims = " \t\n\r";
const char* token = strtok(config, delims);
while (token) {
const char* valueStr = strtok(0, delims);
if (valueStr == 0 || ! (valueStr[0] == '-' || (valueStr[0] >= '0' && valueStr[0] <= '9'))) {
printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", valueStr ? valueStr : "");
return;
}
int value = atoi(valueStr);
if (strcmp(token, "MaxLights") == 0)
Resources.maxLights = value;
else if (strcmp(token, "MaxClipPlanes") == 0)
Resources.maxClipPlanes = value;
else if (strcmp(token, "MaxTextureUnits") == 0)
Resources.maxTextureUnits = value;
else if (strcmp(token, "MaxTextureCoords") == 0)
Resources.maxTextureCoords = value;
else if (strcmp(token, "MaxVertexAttribs") == 0)
Resources.maxVertexAttribs = value;
else if (strcmp(token, "MaxVertexUniformComponents") == 0)
Resources.maxVertexUniformComponents = value;
else if (strcmp(token, "MaxVaryingFloats") == 0)
Resources.maxVaryingFloats = value;
else if (strcmp(token, "MaxVertexTextureImageUnits") == 0)
Resources.maxVertexTextureImageUnits = value;
else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0)
Resources.maxCombinedTextureImageUnits = value;
else if (strcmp(token, "MaxTextureImageUnits") == 0)
Resources.maxTextureImageUnits = value;
else if (strcmp(token, "MaxFragmentUniformComponents") == 0)
Resources.maxFragmentUniformComponents = value;
else if (strcmp(token, "MaxDrawBuffers") == 0)
Resources.maxDrawBuffers = value;
else if (strcmp(token, "MaxVertexUniformVectors") == 0)
Resources.maxVertexUniformVectors = value;
else if (strcmp(token, "MaxVaryingVectors") == 0)
Resources.maxVaryingVectors = value;
else if (strcmp(token, "MaxFragmentUniformVectors") == 0)
Resources.maxFragmentUniformVectors = value;
else if (strcmp(token, "MaxVertexOutputVectors") == 0)
Resources.maxVertexOutputVectors = value;
else if (strcmp(token, "MaxFragmentInputVectors") == 0)
Resources.maxFragmentInputVectors = value;
else if (strcmp(token, "MinProgramTexelOffset") == 0)
Resources.minProgramTexelOffset = value;
else if (strcmp(token, "MaxProgramTexelOffset") == 0)
Resources.maxProgramTexelOffset = value;
else if (strcmp(token, "MaxClipDistances") == 0)
Resources.maxClipDistances = value;
else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0)
Resources.maxComputeWorkGroupCountX = value;
else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0)
Resources.maxComputeWorkGroupCountY = value;
else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0)
Resources.maxComputeWorkGroupCountZ = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0)
Resources.maxComputeWorkGroupSizeX = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0)
Resources.maxComputeWorkGroupSizeY = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0)
Resources.maxComputeWorkGroupSizeZ = value;
else if (strcmp(token, "MaxComputeUniformComponents") == 0)
Resources.maxComputeUniformComponents = value;
else if (strcmp(token, "MaxComputeTextureImageUnits") == 0)
Resources.maxComputeTextureImageUnits = value;
else if (strcmp(token, "MaxComputeImageUniforms") == 0)
Resources.maxComputeImageUniforms = value;
else if (strcmp(token, "MaxComputeAtomicCounters") == 0)
Resources.maxComputeAtomicCounters = value;
else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0)
Resources.maxComputeAtomicCounterBuffers = value;
else if (strcmp(token, "MaxVaryingComponents") == 0)
Resources.maxVaryingComponents = value;
else if (strcmp(token, "MaxVertexOutputComponents") == 0)
Resources.maxVertexOutputComponents = value;
else if (strcmp(token, "MaxGeometryInputComponents") == 0)
Resources.maxGeometryInputComponents = value;
else if (strcmp(token, "MaxGeometryOutputComponents") == 0)
Resources.maxGeometryOutputComponents = value;
else if (strcmp(token, "MaxFragmentInputComponents") == 0)
Resources.maxFragmentInputComponents = value;
else if (strcmp(token, "MaxImageUnits") == 0)
Resources.maxImageUnits = value;
else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0)
Resources.maxCombinedImageUnitsAndFragmentOutputs = value;
else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0)
Resources.maxCombinedShaderOutputResources = value;
else if (strcmp(token, "MaxImageSamples") == 0)
Resources.maxImageSamples = value;
else if (strcmp(token, "MaxVertexImageUniforms") == 0)
Resources.maxVertexImageUniforms = value;
else if (strcmp(token, "MaxTessControlImageUniforms") == 0)
Resources.maxTessControlImageUniforms = value;
else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0)
Resources.maxTessEvaluationImageUniforms = value;
else if (strcmp(token, "MaxGeometryImageUniforms") == 0)
Resources.maxGeometryImageUniforms = value;
else if (strcmp(token, "MaxFragmentImageUniforms") == 0)
Resources.maxFragmentImageUniforms = value;
else if (strcmp(token, "MaxCombinedImageUniforms") == 0)
Resources.maxCombinedImageUniforms = value;
else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0)
Resources.maxGeometryTextureImageUnits = value;
else if (strcmp(token, "MaxGeometryOutputVertices") == 0)
Resources.maxGeometryOutputVertices = value;
else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0)
Resources.maxGeometryTotalOutputComponents = value;
else if (strcmp(token, "MaxGeometryUniformComponents") == 0)
Resources.maxGeometryUniformComponents = value;
else if (strcmp(token, "MaxGeometryVaryingComponents") == 0)
Resources.maxGeometryVaryingComponents = value;
else if (strcmp(token, "MaxTessControlInputComponents") == 0)
Resources.maxTessControlInputComponents = value;
else if (strcmp(token, "MaxTessControlOutputComponents") == 0)
Resources.maxTessControlOutputComponents = value;
else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0)
Resources.maxTessControlTextureImageUnits = value;
else if (strcmp(token, "MaxTessControlUniformComponents") == 0)
Resources.maxTessControlUniformComponents = value;
else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0)
Resources.maxTessControlTotalOutputComponents = value;
else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0)
Resources.maxTessEvaluationInputComponents = value;
else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0)
Resources.maxTessEvaluationOutputComponents = value;
else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0)
Resources.maxTessEvaluationTextureImageUnits = value;
else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0)
Resources.maxTessEvaluationUniformComponents = value;
else if (strcmp(token, "MaxTessPatchComponents") == 0)
Resources.maxTessPatchComponents = value;
else if (strcmp(token, "MaxPatchVertices") == 0)
Resources.maxPatchVertices = value;
else if (strcmp(token, "MaxTessGenLevel") == 0)
Resources.maxTessGenLevel = value;
else if (strcmp(token, "MaxViewports") == 0)
Resources.maxViewports = value;
else if (strcmp(token, "MaxVertexAtomicCounters") == 0)
Resources.maxVertexAtomicCounters = value;
else if (strcmp(token, "MaxTessControlAtomicCounters") == 0)
Resources.maxTessControlAtomicCounters = value;
else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0)
Resources.maxTessEvaluationAtomicCounters = value;
else if (strcmp(token, "MaxGeometryAtomicCounters") == 0)
Resources.maxGeometryAtomicCounters = value;
else if (strcmp(token, "MaxFragmentAtomicCounters") == 0)
Resources.maxFragmentAtomicCounters = value;
else if (strcmp(token, "MaxCombinedAtomicCounters") == 0)
Resources.maxCombinedAtomicCounters = value;
else if (strcmp(token, "MaxAtomicCounterBindings") == 0)
Resources.maxAtomicCounterBindings = value;
else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0)
Resources.maxVertexAtomicCounterBuffers = value;
else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0)
Resources.maxTessControlAtomicCounterBuffers = value;
else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0)
Resources.maxTessEvaluationAtomicCounterBuffers = value;
else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0)
Resources.maxGeometryAtomicCounterBuffers = value;
else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0)
Resources.maxFragmentAtomicCounterBuffers = value;
else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0)
Resources.maxCombinedAtomicCounterBuffers = value;
else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0)
Resources.maxAtomicCounterBufferSize = value;
else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0)
Resources.maxTransformFeedbackBuffers = value;
else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") == 0)
Resources.maxTransformFeedbackInterleavedComponents = value;
else if (strcmp(token, "MaxCullDistances") == 0)
Resources.maxCullDistances = value;
else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0)
Resources.maxCombinedClipAndCullDistances = value;
else if (strcmp(token, "MaxSamples") == 0)
Resources.maxSamples = value;
else if (strcmp(token, "nonInductiveForLoops") == 0)
Resources.limits.nonInductiveForLoops = (value != 0);
else if (strcmp(token, "whileLoops") == 0)
Resources.limits.whileLoops = (value != 0);
else if (strcmp(token, "doWhileLoops") == 0)
Resources.limits.doWhileLoops = (value != 0);
else if (strcmp(token, "generalUniformIndexing") == 0)
Resources.limits.generalUniformIndexing = (value != 0);
else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0)
Resources.limits.generalAttributeMatrixVectorIndexing = (value != 0);
else if (strcmp(token, "generalVaryingIndexing") == 0)
Resources.limits.generalVaryingIndexing = (value != 0);
else if (strcmp(token, "generalSamplerIndexing") == 0)
Resources.limits.generalSamplerIndexing = (value != 0);
else if (strcmp(token, "generalVariableIndexing") == 0)
Resources.limits.generalVariableIndexing = (value != 0);
else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0)
Resources.limits.generalConstantMatrixVectorIndexing = (value != 0);
else
printf("Warning: unrecognized limit (%s) in configuration file.\n", token);
token = strtok(0, delims);
}
if (configStrings)
FreeFileData(configStrings);
}
void VkTestFramework::SetMessageOptions(EShMessages& messages)
{
if (m_compile_options & EOptionRelaxedErrors)
messages = (EShMessages)(messages | EShMsgRelaxedErrors);
if (m_compile_options & EOptionIntermediate)
messages = (EShMessages)(messages | EShMsgAST);
if (m_compile_options & EOptionSuppressWarnings)
messages = (EShMessages)(messages | EShMsgSuppressWarnings);
}
//
// Malloc a string of sufficient size and read a string into it.
//
char** VkTestFramework::ReadFileData(const char* fileName)
{
FILE *in;
#if defined(_WIN32) && defined(__GNUC__)
in = fopen(fileName, "r");
int errorCode = in ? 0 : 1;
#else
int errorCode = fopen_s(&in, fileName, "r");
#endif
char *fdata;
int count = 0;
const int maxSourceStrings = 5;
char** return_data = (char**)malloc(sizeof(char *) * (maxSourceStrings+1));
if (errorCode) {
printf("Error: unable to open input file: %s\n", fileName);
return 0;
}
while (fgetc(in) != EOF)
count++;
fseek(in, 0, SEEK_SET);
if (!(fdata = (char*)malloc(count+2))) {
printf("Error allocating memory\n");
return 0;
}
if (fread(fdata,1,count, in)!=count) {
printf("Error reading input file: %s\n", fileName);
return 0;
}
fdata[count] = '\0';
fclose(in);
if (count == 0) {
return_data[0]=(char*)malloc(count+2);
return_data[0][0]='\0';
m_num_shader_strings = 0;
return return_data;
} else
m_num_shader_strings = 1;
int len = (int)(ceil)((float)count/(float)m_num_shader_strings);
int ptr_len=0,i=0;
while(count>0){
return_data[i]=(char*)malloc(len+2);
memcpy(return_data[i],fdata+ptr_len,len);
return_data[i][len]='\0';
count-=(len);
ptr_len+=(len);
if(count<len){
if(count==0){
m_num_shader_strings=(i+1);
break;
}
len = count;
}
++i;
}
return return_data;
}
void VkTestFramework::FreeFileData(char** data)
{
for(int i=0;i<m_num_shader_strings;i++)
free(data[i]);
}
//
// Deduce the language from the filename. Files must end in one of the
// following extensions:
//
// .vert = vertex
// .tesc = tessellation control
// .tese = tessellation evaluation
// .geom = geometry
// .frag = fragment
// .comp = compute
//
EShLanguage VkTestFramework::FindLanguage(const std::string& name)
{
size_t ext = name.rfind('.');
if (ext == std::string::npos) {
return EShLangVertex;
}
std::string suffix = name.substr(ext + 1, std::string::npos);
if (suffix == "vert")
return EShLangVertex;
else if (suffix == "tesc")
return EShLangTessControl;
else if (suffix == "tese")
return EShLangTessEvaluation;
else if (suffix == "geom")
return EShLangGeometry;
else if (suffix == "frag")
return EShLangFragment;
else if (suffix == "comp")
return EShLangCompute;
return EShLangVertex;
}
//
// Convert VK shader type to compiler's
//
EShLanguage VkTestFramework::FindLanguage(const VkShaderStage shader_type)
{
switch (shader_type) {
case VK_SHADER_STAGE_VERTEX:
return EShLangVertex;
case VK_SHADER_STAGE_TESS_CONTROL:
return EShLangTessControl;
case VK_SHADER_STAGE_TESS_EVALUATION:
return EShLangTessEvaluation;
case VK_SHADER_STAGE_GEOMETRY:
return EShLangGeometry;
case VK_SHADER_STAGE_FRAGMENT:
return EShLangFragment;
case VK_SHADER_STAGE_COMPUTE:
return EShLangCompute;
default:
return EShLangVertex;
}
}
//
// Compile a given string containing GLSL into SPV for use by VK
// Return value of false means an error was encountered.
//
bool VkTestFramework::GLSLtoSPV(const VkShaderStage shader_type,
const char *pshader,
std::vector<unsigned int> &spirv)
{
glslang::TProgram program;
const char *shaderStrings[1];
// TODO: Do we want to load a special config file depending on the
// shader source? Optional name maybe?
// SetConfigFile(fileName);
ProcessConfigFile();
EShMessages messages = EShMsgDefault;
SetMessageOptions(messages);
EShLanguage stage = FindLanguage(shader_type);
glslang::TShader* shader = new glslang::TShader(stage);
shaderStrings[0] = pshader;
shader->setStrings(shaderStrings, 1);
if (! shader->parse(&Resources, (m_compile_options & EOptionDefaultDesktop) ? 110 : 100, false, messages)) {
if (! (m_compile_options & EOptionSuppressInfolog)) {
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
}
return false; // something didn't work
}
program.addShader(shader);
//
// Program-level processing...
//
if (! program.link(messages)) {
if (! (m_compile_options & EOptionSuppressInfolog)) {
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
}
return false;
}
if (m_compile_options & EOptionDumpReflection) {
program.buildReflection();
program.dumpReflection();
}
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
//
// Test the different modes of SPIR-V modification
//
if (this->m_canonicalize_spv) {
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::ALL_BUT_STRIP);
}
if (this->m_strip_spv) {
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::STRIP);
}
if (this->m_do_everything_spv) {
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::DO_EVERYTHING);
}
delete shader;
return true;
}
VkTestImageRecord::VkTestImageRecord() : // Constructor
m_width( 0 ),
m_height( 0 ),
m_data( NULL ),
m_data_size( 0 )
{
}
VkTestImageRecord::~VkTestImageRecord()
{
}
VkTestImageRecord::VkTestImageRecord(const VkTestImageRecord &copyin) // Copy constructor to handle pass by value.
{
m_title = copyin.m_title;
m_width = copyin.m_width;
m_height = copyin.m_height;
m_data_size = copyin.m_data_size;
m_data = copyin.m_data; // TODO: Do we need to copy the data or is pointer okay?
}
ostream &operator<<(ostream &output, const VkTestImageRecord &VkTestImageRecord)
{
output << VkTestImageRecord.m_title << " (" << VkTestImageRecord.m_width <<
"," << VkTestImageRecord.m_height << ")" << endl;
return output;
}
VkTestImageRecord& VkTestImageRecord::operator=(const VkTestImageRecord &rhs)
{
m_title = rhs.m_title;
m_width = rhs.m_width;
m_height = rhs.m_height;
m_data_size = rhs.m_data_size;
m_data = rhs.m_data;
return *this;
}
int VkTestImageRecord::operator==(const VkTestImageRecord &rhs) const
{
if( this->m_data != rhs.m_data) return 0;
return 1;
}
// This function is required for built-in STL list functions like sort
int VkTestImageRecord::operator<(const VkTestImageRecord &rhs) const
{
if( this->m_data_size < rhs.m_data_size ) return 1;
return 0;
}