| // 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 "GL/freeglut_std.h" |
| //#include "ShaderLang.h" |
| #include "GlslangToSpv.h" |
| #include <limits.h> |
| #include <math.h> |
| #include <wand/MagickWand.h> |
| #include <xcb/xcb.h> |
| #include <vk_wsi_lunarg.h> |
| |
| // 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, |
| }; |
| |
| class TestFrameworkVkPresent |
| { |
| public: |
| TestFrameworkVkPresent(vk_testing::Device &device); |
| |
| void Run(); |
| void InitPresentFramework(std::list<VkTestImageRecord> &imagesIn); |
| void CreateMyWindow(); |
| void CreateSwapChain(); |
| void TearDown(); |
| |
| protected: |
| vk_testing::Device &m_device; |
| vk_testing::Queue &m_queue; |
| vk_testing::CmdBuffer m_cmdbuf; |
| |
| private: |
| 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; |
| std::list<VkTestImageRecord> m_images; |
| |
| VkSwapChainWSI m_swap_chain; |
| |
| bool m_quit; |
| bool m_pause; |
| |
| uint32_t m_width; |
| uint32_t m_height; |
| |
| std::list<VkTestImageRecord>::iterator m_display_image; |
| |
| void Display(); |
| void HandleEvent(xcb_generic_event_t *event); |
| }; |
| |
| #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; |
| #ifdef _WIN32 |
| bool VkTestFramework::m_use_spv = false; |
| #else |
| bool VkTestFramework::m_use_spv = true; |
| #endif |
| 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; |
| |
| void VkTestFramework::InitArgs(int *argc, char *argv[]) |
| { |
| int i, n; |
| |
| for (i=0, n=0; i< *argc; i++) { |
| if (strncmp("--show-images", argv[i], 13) == 0) { |
| m_show_images = true; |
| continue; |
| } |
| if (strncmp("--save-images", argv[i], 13) == 0) { |
| m_save_images = true; |
| continue; |
| } |
| |
| if (strncmp("--use-SPV", argv[i], 13) == 0) { |
| m_use_spv = true; |
| continue; |
| } |
| |
| if (strncmp("--no-SPV", argv[i], 13) == 0) { |
| m_use_spv = false; |
| continue; |
| } |
| |
| if (strncmp("--compare-images", argv[i], 16) == 0) { |
| m_compare_images = true; |
| continue; |
| } |
| |
| /* |
| * Since the above "consume" inputs, update argv |
| * so that it contains the trimmed list of args for glutInit |
| */ |
| if (strncmp("--help", argv[i], 6) == 0 || strncmp("-h", argv[i], 2) == 0) { |
| 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--use-SPV\n" |
| "\t\tUse SPV code path (default).\n"); |
| printf("\t--no-SPV\n" |
| "\t\tUse built-in GLSL compiler rather than SPV code path.\n"); |
| exit(0); |
| } |
| |
| argv[n] = argv[i]; |
| n++; |
| } |
| } |
| |
| void VkTestFramework::WritePPM( const char *basename, VkImageObj *image ) |
| { |
| string filename; |
| VkResult err; |
| int x, y; |
| VkImageObj displayImage(image->device()); |
| VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
| |
| displayImage.init(image->extent().width, image->extent().height, image->format(), 0, 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; |
| size_t data_size = sizeof(sr_layout); |
| |
| err = vkGetImageSubresourceInfo(image->device()->device(), image->image(), &sr, |
| VK_SUBRESOURCE_INFO_TYPE_LAYOUT, |
| &data_size, &sr_layout); |
| ASSERT_VK_SUCCESS( err ); |
| ASSERT_EQ(data_size, sizeof(sr_layout)); |
| |
| char *ptr; |
| ptr = (char *) displayImage.map(); |
| ptr += sr_layout.offset; |
| |
| ofstream file (filename.c_str()); |
| ASSERT_TRUE(file.is_open()) << "Unable to open file: " << filename; |
| |
| file << "P6\n"; |
| file << displayImage.width() << "\n"; |
| 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.unmap(); |
| } |
| |
| 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[PATH_MAX+256],golddir[PATH_MAX] = "./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_TRUE(status) << "Unable to open file: " << testimage; |
| |
| |
| MagickWandGenesis(); |
| magick_wand_2=NewMagickWand(); |
| sprintf(golden,"%s/%s.ppm",golddir,basename); |
| status=MagickReadImage(magick_wand_2,golden); |
| ASSERT_TRUE(status) << "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; |
| |
| const VkImageSubresource sr = { |
| VK_IMAGE_ASPECT_COLOR, 0, 0 |
| }; |
| VkSubresourceLayout sr_layout; |
| size_t data_size = sizeof(sr_layout); |
| VkTestImageRecord record; |
| |
| if (!m_show_images) return; |
| |
| err = vkGetImageSubresourceInfo( image->device()->device(), image->image(), &sr, VK_SUBRESOURCE_INFO_TYPE_LAYOUT, |
| &data_size, &sr_layout); |
| ASSERT_VK_SUCCESS( err ); |
| ASSERT_EQ(data_size, sizeof(sr_layout)); |
| |
| char *ptr; |
| |
| err = image->MapMemory( (void **) &ptr ); |
| ASSERT_VK_SUCCESS( err ); |
| |
| ptr += sr_layout.offset; |
| |
| record.m_title.append(comment); |
| record.m_width = image->width(); |
| record.m_height = image->height(); |
| // TODO: Need to make this more robust to handle different image formats |
| record.m_data_size = image->width()*image->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(); |
| |
| err = image->UnmapMemory(); |
| ASSERT_VK_SUCCESS( err ); |
| |
| } |
| |
| 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_cmdbuf(m_device, vk_testing::CmdBuffer::create_info(m_device.graphics_queue_node_index_)) |
| { |
| m_quit = false; |
| m_pause = false; |
| m_width = 0; |
| m_height = 0; |
| } |
| |
| void TestFrameworkVkPresent::Display() |
| { |
| VkResult U_ASSERT_ONLY err; |
| |
| VkPresentInfoWSI present = {}; |
| present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_WSI; |
| present.image = m_display_image->m_presentableImage; |
| present.flipInterval = 1; |
| |
| 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()); |
| |
| err = vkQueuePresentWSI(m_queue.obj(), &present); |
| assert(!err); |
| |
| m_queue.wait(); |
| |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| void TestFrameworkVkPresent::CreateSwapChain() |
| { |
| VkResult U_ASSERT_ONLY err; |
| |
| VkSwapChainCreateInfoWSI swap_chain = {}; |
| swap_chain.sType = VK_STRUCTURE_TYPE_SWAP_CHAIN_CREATE_INFO_WSI; |
| swap_chain.pNativeWindowSystemHandle = (void *) m_connection; |
| swap_chain.pNativeWindowHandle = (void *) (intptr_t) m_window; |
| swap_chain.imageCount = m_images.size(); |
| swap_chain.imageFormat = VK_FORMAT_B8G8R8A8_UNORM; |
| swap_chain.imageExtent.width = m_width; |
| swap_chain.imageExtent.height = m_height; |
| swap_chain.imageArraySize = 1; |
| swap_chain.imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT; |
| swap_chain.swapModeFlags = VK_SWAP_MODE_FLIP_BIT_WSI | |
| VK_SWAP_MODE_BLIT_BIT_WSI; |
| |
| err = vkCreateSwapChainWSI(m_device.obj(), &swap_chain, &m_swap_chain); |
| assert(!err); |
| |
| size_t size = sizeof(VkSwapChainImageInfoWSI) * m_images.size(); |
| std::vector<VkSwapChainImageInfoWSI> persistent_images; |
| persistent_images.resize(m_images.size()); |
| err = vkGetSwapChainInfoWSI(m_swap_chain, |
| VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_WSI, |
| &size, &persistent_images[0]); |
| assert(!err && size == sizeof(VkSwapChainImageInfoWSI) * m_images.size()); |
| |
| m_display_image = m_images.begin(); |
| |
| for (int x=0; x < m_images.size(); x++) |
| { |
| void *dest_ptr; |
| |
| m_display_image->m_presentableImage = persistent_images[x].image; |
| m_display_image->m_presentableMemory = persistent_images[x].memory; |
| |
| vk_testing::Buffer buf; |
| buf.init(m_device, (VkDeviceSize) m_display_image->m_data_size); |
| dest_ptr = buf.map(); |
| memcpy(dest_ptr,m_display_image->m_data, m_display_image->m_data_size); |
| buf.unmap(); |
| |
| m_cmdbuf.begin(); |
| |
| 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.obj(), |
| buf.obj(), |
| m_display_image->m_presentableImage, VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, |
| 1, ®ion); |
| m_cmdbuf.end(); |
| |
| vkQueueAddMemReferences(m_queue.obj(), 1, &m_display_image->m_presentableMemory); |
| vkQueueAddMemReferences(m_queue.obj(), buf.memories().size(), &buf.memories()[0]); |
| |
| VkCmdBuffer cmdBufs[1]; |
| cmdBufs[0] = m_cmdbuf.obj(); |
| |
| vkQueueSubmit(m_queue.obj(), 1, cmdBufs, NULL); |
| m_queue.wait(); |
| |
| vkQueueRemoveMemReferences(m_queue.obj(), 1, &m_display_image->m_presentableMemory); |
| vkQueueRemoveMemReferences(m_queue.obj(), buf.memories().size(), &buf.memories()[0]); |
| |
| ++m_display_image; |
| } |
| |
| m_display_image = m_images.begin(); |
| } |
| |
| void TestFrameworkVkPresent::InitPresentFramework(std::list<VkTestImageRecord> &imagesIn) |
| { |
| m_images = imagesIn; |
| } |
| |
| 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); |
| } |
| |
| void TestFrameworkVkPresent::TearDown() |
| { |
| vkDestroySwapChainWSI(m_swap_chain); |
| xcb_destroy_window(m_connection, m_window); |
| xcb_disconnect(m_connection); |
| } |
| |
| void VkTestFramework::Finish() |
| { |
| if (m_images.size() == 0) return; |
| |
| vk_testing::Environment env; |
| env.SetUp(); |
| { |
| TestFrameworkVkPresent vkPresent(env.default_device()); |
| |
| vkPresent.InitPresentFramework(m_images); |
| 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 = new char[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> &spv) |
| { |
| glslang::TProgram& program = *new glslang::TProgram; |
| 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), spv); |
| |
| return true; |
| } |
| |
| |
| |
| VkTestImageRecord::VkTestImageRecord() : // Constructor |
| m_width( 0 ), |
| m_height( 0 ), |
| m_data( NULL ), |
| m_presentableImage( NULL ), |
| m_presentableMemory( NULL), |
| m_data_size( 0 ) |
| { |
| } |
| |
| VkTestImageRecord::~VkTestImageRecord() |
| { |
| |
| } |
| |
| VkTestImageRecord::VkTestImageRecord(const VkTestImageRecord ©in) // 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? |
| m_presentableImage = copyin.m_presentableImage; |
| m_presentableMemory = copyin.m_presentableMemory; |
| } |
| |
| 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; |
| m_presentableImage = rhs.m_presentableImage; |
| m_presentableMemory = rhs.m_presentableMemory; |
| 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; |
| } |
| |