diff --git a/src/gallium/frontends/clover/Makefile.sources b/src/gallium/frontends/clover/Makefile.sources index 38f94981fb6..534574178c3 100644 --- a/src/gallium/frontends/clover/Makefile.sources +++ b/src/gallium/frontends/clover/Makefile.sources @@ -31,6 +31,8 @@ CPP_SOURCES := \ core/object.hpp \ core/platform.cpp \ core/platform.hpp \ + core/printf.cpp \ + core/printf.hpp \ core/program.cpp \ core/program.hpp \ core/property.hpp \ diff --git a/src/gallium/frontends/clover/api/device.cpp b/src/gallium/frontends/clover/api/device.cpp index 80101359368..ed36ad51025 100644 --- a/src/gallium/frontends/clover/api/device.cpp +++ b/src/gallium/frontends/clover/api/device.cpp @@ -369,9 +369,7 @@ clGetDeviceInfo(cl_device_id d_dev, cl_device_info param, break; case CL_DEVICE_PRINTF_BUFFER_SIZE: - // Per the spec, the minimum value for the FULL profile is 1 MB. - // However, clover is not ready yet to support it - buf.as_scalar() = 0 /* 1024 */; + buf.as_scalar() = dev.max_printf_buffer_size(); break; case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC: diff --git a/src/gallium/frontends/clover/core/device.cpp b/src/gallium/frontends/clover/core/device.cpp index 0a8fa50d338..4e9268eb3e9 100644 --- a/src/gallium/frontends/clover/core/device.cpp +++ b/src/gallium/frontends/clover/core/device.cpp @@ -204,6 +204,11 @@ device::max_compute_units() const { PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS)[0]; } +cl_uint +device::max_printf_buffer_size() const { + return 1024 * 1024; +} + bool device::image_support() const { return get_compute_param(pipe, ir_format(), diff --git a/src/gallium/frontends/clover/core/device.hpp b/src/gallium/frontends/clover/core/device.hpp index bc57c8d8197..5cf3b6dc6b6 100644 --- a/src/gallium/frontends/clover/core/device.hpp +++ b/src/gallium/frontends/clover/core/device.hpp @@ -70,6 +70,7 @@ namespace clover { cl_ulong max_mem_alloc_size() const; cl_uint max_clock_frequency() const; cl_uint max_compute_units() const; + cl_uint max_printf_buffer_size() const; bool image_support() const; bool has_doubles() const; bool has_halves() const; diff --git a/src/gallium/frontends/clover/core/kernel.cpp b/src/gallium/frontends/clover/core/kernel.cpp index e9169317199..0ca34cb7643 100644 --- a/src/gallium/frontends/clover/core/kernel.cpp +++ b/src/gallium/frontends/clover/core/kernel.cpp @@ -173,7 +173,7 @@ kernel::module(const command_queue &q) const { } kernel::exec_context::exec_context(kernel &kern) : - kern(kern), q(NULL), mem_local(0), st(NULL), cs() { + kern(kern), q(NULL), print_handler(), mem_local(0), st(NULL), cs() { } kernel::exec_context::~exec_context() { @@ -251,6 +251,17 @@ kernel::exec_context::bind(intrusive_ptr _q, arg->bind(*this, marg); break; } + case module::argument::printf_buffer: { + print_handler = printf_handler::create(q, m.printf_infos, + m.printf_strings_in_buffer, + q->device().max_printf_buffer_size()); + cl_mem print_mem = print_handler->get_mem(); + + auto arg = argument::create(marg); + arg->set(sizeof(cl_mem), &print_mem); + arg->bind(*this, marg); + break; + } } } @@ -277,6 +288,9 @@ kernel::exec_context::bind(intrusive_ptr _q, void kernel::exec_context::unbind() { + if (print_handler) + print_handler->print(); + for (auto &arg : kern.args()) arg.unbind(*this); diff --git a/src/gallium/frontends/clover/core/kernel.hpp b/src/gallium/frontends/clover/core/kernel.hpp index 31967b66597..57627e983bc 100644 --- a/src/gallium/frontends/clover/core/kernel.hpp +++ b/src/gallium/frontends/clover/core/kernel.hpp @@ -27,6 +27,7 @@ #include #include "core/object.hpp" +#include "core/printf.hpp" #include "core/program.hpp" #include "core/memory.hpp" #include "core/sampler.hpp" @@ -53,6 +54,7 @@ namespace clover { kernel &kern; intrusive_ptr q; + std::unique_ptr print_handler; std::vector input; std::vector samplers; diff --git a/src/gallium/frontends/clover/core/module.hpp b/src/gallium/frontends/clover/core/module.hpp index 01694b83222..1d1f803520f 100644 --- a/src/gallium/frontends/clover/core/module.hpp +++ b/src/gallium/frontends/clover/core/module.hpp @@ -103,7 +103,8 @@ namespace clover { grid_offset, image_size, image_format, - constant_buffer + constant_buffer, + printf_buffer }; argument(enum type type, size_t size, diff --git a/src/gallium/frontends/clover/core/printf.cpp b/src/gallium/frontends/clover/core/printf.cpp new file mode 100644 index 00000000000..d4ec96bd581 --- /dev/null +++ b/src/gallium/frontends/clover/core/printf.cpp @@ -0,0 +1,237 @@ +// +// Copyright 2020 Serge Martin +// +// 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 +#include +#include +#include + +#include "util/u_math.h" +#include "core/printf.hpp" + +#include "util/u_printf.h" +using namespace clover; + +namespace { + + const cl_uint hdr_dwords = 2; + const cl_uint initial_buffer_offset = hdr_dwords * sizeof(cl_uint); + + /* all valid chars that can appear in CL C printf string. */ + const std::string clc_printf_whitelist = "%0123456789-+ #.AacdeEfFgGhilopsuvxX"; + + void + print_formatted(const std::vector &formatters, + bool _strings_in_buffer, + const std::vector &buffer) { + + static std::atomic warn_count; + if (buffer.empty() && !warn_count++) + std::cerr << "Printf used but no printf occurred - may cause perfomance issue." << std::endl; + + for (size_t buf_pos = 0; buf_pos < buffer.size(); ) { + cl_uint fmt_idx = *(cl_uint*)&buffer[buf_pos]; + assert(fmt_idx > 0); + module::printf_info fmt = formatters[fmt_idx-1]; + + std::string format = (char *)fmt.strings.data(); + buf_pos += sizeof(cl_uint); + + if (fmt.arg_sizes.empty()) { + printf("%s", format.c_str()); + + } else { + size_t fmt_last_pos = 0; + size_t fmt_pos = 0; + for (int arg_size : fmt.arg_sizes) { + const size_t spec_pos = util_printf_next_spec_pos(format, fmt_pos); + const size_t cur_tok = format.rfind('%', spec_pos); + const size_t next_spec = util_printf_next_spec_pos(format, spec_pos); + const size_t next_tok = next_spec == std::string::npos ? std::string::npos : + format.rfind('%', next_spec); + + size_t vec_pos = format.find_first_of("v", cur_tok + 1); + size_t mod_pos = format.find_first_of("hl", cur_tok + 1); + + // print the part before the format token + if (cur_tok != fmt_last_pos) { + std::string s = format.substr(fmt_last_pos, + cur_tok - fmt_last_pos); + printf("%s", s.c_str()); + } + + std::string print_str; + print_str = format.substr(cur_tok, spec_pos + 1 - cur_tok); + + /* Never pass a 'n' spec to the host printf */ + bool valid_str = print_str.find_first_not_of(clc_printf_whitelist) == + std::string::npos; + + // print the formated part + if (spec_pos != std::string::npos && valid_str) { + bool is_vector = vec_pos != std::string::npos && + vec_pos + 1 < spec_pos; + bool is_string = format[spec_pos] == 's'; + bool is_float = std::string("fFeEgGaA") + .find(format[spec_pos]) != std::string::npos; + + if (is_string) { + if (_strings_in_buffer) + printf(print_str.c_str(), &buffer[buf_pos]); + else { + uint64_t idx; + memcpy(&idx, &buffer[buf_pos], 8); + printf(print_str.c_str(), &fmt.strings[idx]); + } + } else { + int component_count = 1; + + if (is_vector) { + size_t l = std::min(mod_pos, spec_pos) - vec_pos - 1; + std::string s = format.substr(vec_pos + 1, l); + component_count = std::stoi(s); + if (mod_pos != std::string::npos) { + // CL C has hl specifier for 32-bit vectors, C doesn't have it + // just remove it. + std::string mod = format.substr(mod_pos, 2); + if (mod == "hl") + mod_pos = std::string::npos; + } + print_str.erase(vec_pos - cur_tok, std::min(mod_pos, spec_pos) - vec_pos); + print_str.push_back(','); + } + + //in fact vec3 are vec4 + int men_components = + component_count == 3 ? 4 : component_count; + size_t elmt_size = arg_size / men_components; + + for (int i = 0; i < component_count; i++) { + size_t elmt_buf_pos = buf_pos + i * elmt_size; + if (is_vector && i + 1 == component_count) + print_str.pop_back(); + + if (is_float) { + switch (elmt_size) { + case 2: + cl_half h; + std::memcpy(&h, &buffer[elmt_buf_pos], elmt_size); + printf(print_str.c_str(), h); + break; + case 4: + cl_float f; + std::memcpy(&f, &buffer[elmt_buf_pos], elmt_size); + printf(print_str.c_str(), f); + break; + default: + cl_double d; + std::memcpy(&d, &buffer[elmt_buf_pos], elmt_size); + printf(print_str.c_str(), d); + } + } else { + cl_long l = 0; + std::memcpy(&l, &buffer[elmt_buf_pos], elmt_size); + printf(print_str.c_str(), l); + } + } + } + // print the remaining + if (next_tok != spec_pos) { + std::string s = format.substr(spec_pos + 1, + next_tok - spec_pos - 1); + printf("%s", s.c_str()); + } + } + + fmt_pos = spec_pos; + fmt_last_pos = next_tok; + + buf_pos += arg_size; + buf_pos = ALIGN(buf_pos, 4); + } + } + } + } +} + +std::unique_ptr +printf_handler::create(const intrusive_ptr &q, + const std::vector &infos, + bool strings_in_buffer, + cl_uint size) { + return std::unique_ptr( + new printf_handler(q, infos, strings_in_buffer, size)); +} + +printf_handler::printf_handler(const intrusive_ptr &q, + const std::vector &infos, + bool strings_in_buffer, + cl_uint size) : + _q(q), _formatters(infos), _strings_in_buffer(strings_in_buffer), _size(size), _buffer() { + + if (_size) { + std::string data; + data.reserve(_size); + cl_uint header[2] = { 0 }; + + header[0] = initial_buffer_offset; + header[1] = _size; + + data.append((char *)header, (char *)(header+hdr_dwords)); + _buffer = std::unique_ptr(new root_buffer(_q->context, + std::vector(), + CL_MEM_COPY_HOST_PTR, + _size, (char*)data.data())); + } +} + +cl_mem +printf_handler::get_mem() { + return (cl_mem)(_buffer.get()); +} + +void +printf_handler::print() { + if (!_buffer) + return; + + mapping src = { *_q, _buffer->resource_in(*_q), CL_MAP_READ, true, + {{ 0 }}, {{ _size, 1, 1 }} }; + + cl_uint header[2] = { 0 }; + std::memcpy(header, + static_cast(src), + initial_buffer_offset); + + cl_uint buffer_size = header[0]; + buffer_size -= initial_buffer_offset; + std::vector buf; + buf.resize(buffer_size); + + std::memcpy(buf.data(), + static_cast(src) + initial_buffer_offset, + buffer_size); + + // mixed endian isn't going to work, sort it out if anyone cares later. + assert(_q->device().endianness() == PIPE_ENDIAN_NATIVE); + print_formatted(_formatters, _strings_in_buffer, buf); +} diff --git a/src/gallium/frontends/clover/core/printf.hpp b/src/gallium/frontends/clover/core/printf.hpp new file mode 100644 index 00000000000..3fc740c1aea --- /dev/null +++ b/src/gallium/frontends/clover/core/printf.hpp @@ -0,0 +1,60 @@ +// +// Copyright 2020 Serge Martin +// +// 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. +// + +#ifndef CLOVER_CORE_PRINTF_HANDLER_HPP +#define CLOVER_CORE_PRINTF_HANDLER_HPP + +#include + +#include "core/memory.hpp" + +namespace clover { + class printf_handler { + public: + static std::unique_ptr + create(const intrusive_ptr &q, + const std::vector &info, + bool strings_in_buffer, cl_uint size); + + printf_handler(const printf_handler &arg) = delete; + printf_handler & + operator=(const printf_handler &arg) = delete; + + ~printf_handler() {}; + + cl_mem get_mem(); + void print(); + + private: + printf_handler(const intrusive_ptr &q, + const std::vector &infos, + bool strings_in_buffer, cl_uint size); + + intrusive_ptr _q; + std::vector _formatters; + bool _strings_in_buffer; + cl_uint _size; + std::unique_ptr _buffer; + }; +} + +#endif diff --git a/src/gallium/frontends/clover/meson.build b/src/gallium/frontends/clover/meson.build index b946eefff37..b6a231f9790 100644 --- a/src/gallium/frontends/clover/meson.build +++ b/src/gallium/frontends/clover/meson.build @@ -132,6 +132,8 @@ clover_files = files( 'core/object.hpp', 'core/platform.cpp', 'core/platform.hpp', + 'core/printf.cpp', + 'core/printf.hpp', 'core/program.cpp', 'core/program.hpp', 'core/property.hpp',