-
Notifications
You must be signed in to change notification settings - Fork 725
CLI Samples launcher + Vulkan Hello Triangle #493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CLI Samples launcher + Vulkan Hello Triangle #493
Conversation
5184c1f
to
fa323b8
Compare
fa323b8
to
e385ce4
Compare
1a3f091
to
0311ace
Compare
0311ace
to
6f61eb1
Compare
@@ -32,6 +32,8 @@ function(vkb__register_component) | |||
|
|||
add_library("vkb__${TARGET_NAME}" STATIC ${TARGET_SRC}) | |||
|
|||
set_property(TARGET "vkb__${TARGET_NAME}" PROPERTY POSITION_INDEPENDENT_CODE ON) # PIC required for shared lib linking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is probably better to use set_compile_options("vkb_${TARGET_NAME}" PRIVATE POSITION_INDEPENDENT_CODE ON)
NB: prior to CMake 3.19, -fPIC didn't mean that pie is also by default mandated in cmake other than in Android builds. I think we're mandating a recent enough version for this to not be an issue. Just listing here as a just incase.
endif() | ||
|
||
if(MSVC) | ||
target_compile_options("sample__${TARGET_NAME}" PRIVATE /W4 /WX) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dunno if you get any warnings in MSVC when compiling with this due to /W3 and /Md being set automatically D9025 might crop up. However, just in case it happens:
https://cmake.org/cmake/help/v3.15/policy/CMP0092.html
https://cmake.org/cmake/help/v3.15/policy/CMP0091.html
add:
if (POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
endif ()
if (POLICY CMP0092)
cmake_policy(SET CMP0092 NEW)
endif ()
* event bus * add AbstractChannel comment * response to feedback * add assert * assert * added observer expiration test
* fix folder structure in Visual Studio * add interface library support * review changes
* std filesystem more tests and fixes recursive tests more tests remove order specific checks from tests review comments * fix duplicated check * review changes
* event bus * response to feedback * add assert * assert * added observer expiration test * add pipelines * stages with custom fields * Pipeline -> EventPipeline * add comments
6637af6
to
311b2b9
Compare
message("ADDING STATIC: vkb__${TARGET_NAME}") | ||
|
||
add_library("vkb__${TARGET_NAME}" STATIC ${TARGET_SRC}) | ||
add_library("vkb__${TARGET_NAME}" STATIC ${TARGET_SRC} ${TARGET_HEADERS}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
think this is the same as in 490? Not sure of the advantage of adding headers explicitly to the cmake target.
SHADER_FILES_GLSL | ||
${TARGET_SHADER_FILES_GLSL}) | ||
get_property(DESCRIPTORS GLOBAL PROPERTY SAMPLE_DESCRIPTORS) | ||
list(APPEND DESCRIPTORS "{\"id\":\"${TARGET_ID}\",\"name\":\"${TARGET_NAME}\", \"description\": \"${TARGET_DESCRIPTION}\", \"library_name\": \"${TARGET_LIB}\"}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could potentially use jq or https://github.com/sbellus/json-cmake or some such to make this a usable thing at the cmake level. however, this works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this allow for creating json structs in cmake? Or just parsing?
@SaschaWillems @gary-sweet @asuessenbach can you please take a look at this PR when you get a chance? Looking for general feedback on how these components fit together and how samples can be written. The idea is to remove all the constraints on samples so that they can be more flexible in the future. An example of this is |
First of all: the framework compiles without any issues with VS2022.
And, indeed, that path does not exist. I found the file |
}; | ||
|
||
// Load a config from a binary buffer | ||
components::StackErrorPtr load_config_from_json(const std::vector<uint8_t> &data, Config *config); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, we agreed to switch to std::exception
s here, instead of components::StackErrorPtr
?
(Funnily, components::StackErrorPtr
derives from std::exception
, but is not used by the throw
/catch
mechanism of exceptions!)
|
||
std::cout << "loading sample configs\n"; | ||
|
||
std::vector<std::string> files; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename files
to config_files
, or so?
|
||
std::cout << "looking for: " << library_name; | ||
|
||
std::vector<std::string> candidates; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you expect more than one candidate per sample? But you just use the first one.
Maybe the search in the sample_files could then be done by std::find_if
:
auto sample_it = std::find_if(sample_files.begin(),
sample_files.end(),
[&library_name](auto const &sample_file) { return vfs::helpers::get_file_name(sample_file) == library_name; });
if (sample_it == sample_files.end())
{
std::cout << " - not found\n";
continue;
}
return EXIT_SUCCESS; | ||
} | ||
|
||
for (auto &info : available_samples) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use std::find_if
, again?
auto sample_it =
std::find_if(available_samples.begin(), available_samples.end(), [&args](auto &sample_info) { return sample_info.sample.id == args[0]; });
if (sample_it == available_samples.end())
{
std::cout << "Missing sample " << args[0] << "\n";
}
|
||
#define HANDLE_JSON_ERROR_START() \ | ||
try \ | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not hide scope opening in a macro. Functions like marshall_json below look somewhat awkward then.
Maybe just some HANDLE_JSON_ERROR representing the catch block would be enough?
} | ||
|
||
template <> | ||
class JsonMarshaler<config::Sample> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The termini Marshaler
and UnMarshaler
are a bit alien to me.
Maybe something like Serializer
/Deserializer
or Encoder
/Decoder
would be a bit more descriptive?
{ | ||
touch.position.x = event.pos_x - touch.position.x; | ||
touch.position.y = event.pos_y - touch.position.y; | ||
touch.position.x = event.x - touch.position.x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this supposed to do?
It's unconditionally overwritten next.
|
||
inline size_t unobserved_each_event_count() const | ||
{ | ||
size_t unprocessed_events{0}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
return std::accumulate(
m_each_callbacks.begin(), m_each_callbacks.end(), 0, []( size_t acc, auto const & callback ) { return acc + callback.second->queue_size(); } );
And similar for the other count functions.
// on a second pass we expect only the "then" stages to be processed | ||
execution_index = 2; | ||
|
||
pipeline.process(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how the correct order is verified. Could you please elaborate a bit?
|
||
REQUIRE(one_execution_count == 1); | ||
REQUIRE(two_execution_count == 4); | ||
REQUIRE(three_execution_count == 4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, the number of calls of each event is checked, but how is the correct order verified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Windows using VS, I had to set the Working Directory to the root of the repository in order to find everything. I don't see that mentioned anywhere.
# undef IMPORT | ||
# define EXPORT __declspec(dllexport) | ||
# define IMPORT __declspec(dllimport) | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe better to read:
#if defined(_WIN32)
# define EXPORT __declspec(dllexport)
# define IMPORT __declspec(dllimport)
#else
# define EXPORT
# define IMPORT
#endif
* @param name library name | ||
* @return std::string OS library name | ||
*/ | ||
std::string os_library_name(const std::string &name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixing functions taking a const std::string &
with functions taking a const char *
in the same header seems counter-intuitive. I would stay with one of them, keeping the same level of abstraction.
*/ | ||
struct PlatformContext | ||
{ | ||
virtual std::vector<std::string> arguments() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you make this function pure virtual?
Probably all Context types just hold the arguments in a vector, that is it could be as well be moved into PlatformContext
. Then, you don't even need that accessor function.
* | ||
* @return int status code | ||
*/ | ||
int platform_main(components::PlatformContext *); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't platform_main
take a const &
, or maybe a const *
?
std::stringstream lib_name; | ||
lib_name << os_library_prefix() << name << os_library_postfix(); | ||
return lib_name.str(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to use a std::stringstream
here. Simply
return os_library_prefix() + name + os_library_postfix();
|
||
if (!validate_extensions(required_device_extensions, device_extensions)) | ||
{ | ||
throw std::runtime_error("Required device extensions are missing, will try without."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean with "will try without" ?
|
||
bool validate_extensions(const std::vector<const char *> & required, | ||
const std::vector<VkExtensionProperties> &available) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
// inner find_if returns true if the extension was not found
// outer find_if returns true if none of the extensions were not found, that is if all extensions were found
return std::find_if(required.begin(),
required.end(),
[&available](auto required_extension) {
return std::find_if(available.begin(),
available.end(),
[&required_extension](auto const &available_extension) {
return strcmp(available_extension.extensionName, required_extension) == 0;
}) == available.end();
}) == required.end();
bool validate_layers(const std::vector<const char *> & required, | ||
const std::vector<VkLayerProperties> &available) | ||
{ | ||
for (auto extension : required) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
// inner find_if returns true if the layer was not found
// outer find_if returns true if none of the layers was not found, that is if all layers were found
return std::find_if(required.begin(),
required.end(),
[&available](auto required_layer) {
return std::find_if(available.begin(),
available.end(),
[&required_layer](auto const &available_layer) {
return strcmp(available_layer.layerName, required_layer) == 0;
}) == available.end();
}) == required.end();
{ | ||
LOGI("Initializing vulkan instance."); | ||
|
||
if (volkInitialize()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
if (volkInitialize() != VK_SUCCESS)
} | ||
|
||
format.format = VK_FORMAT_UNDEFINED; | ||
for (auto &candidate : formats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some std::find_if
here?
Description
Blocked by #485 #487 #488 #490
Large diff as this contains commits from several branches
Currently only works on Ubuntu with XLIB enabled as I have not added every window and surface type yet
samples.json
is maintained by CMake automatically and contains metadata for all registered samples. Samples may be available or not, we cant easily track which have been built previously and which haven't. The Sample Launcher deduces available samples by searching the current working directory for platform specific runtime libraries for a given sample. Available samples are then displayed in a list.The code sample shows the launcher being run with
hello_triangle
. The runtime library is found as stated in the lookup log. The launcher then loads this sample and runs itssample_main
.After a little bit more work I ported Hello Triangle
Solved Problems
Compilation
Single samples can be compiled by using the
sample__<sample_name>
target. All samples can be compiled withvkb__samples
.samples_launcher
is only compiled when one or more samples are selected for compilation. A user should never need to manually compilesamples_launcher
.As the framework is now split into components a sample and the launcher will only compile the targets needed. For instance, compiling the current samples yields sub second compile times. There is also no link step so serialized link times in our current builds will not occur in this version of the framework.
Samples
Samples no longer need to follow strict patterns for creation. Requirements for a sample are:
sample_main
vkb__register_sample
- sample build targetvkb__register_sample_descriptor
- sample metadataFrom here samples can come in any shape or size. The most appropriate execution flow can be used for each sample. We may want to define a component which acts as a base sample to reduce boilerplate, but these building blocks do not need to be the same for all samples.
TODO
vkb__samples