| // Copyright 2019 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "SpirvShader.hpp" |
| |
| #ifdef ENABLE_VK_DEBUGGER |
| |
| # include "Vulkan/Debug/Context.hpp" |
| # include "Vulkan/Debug/File.hpp" |
| # include "Vulkan/Debug/Thread.hpp" |
| # include "Vulkan/Debug/Variable.hpp" |
| |
| # include "spirv-tools/libspirv.h" |
| |
| # include <algorithm> |
| |
| namespace spvtools { |
| |
| // Function implemented in third_party/SPIRV-Tools/source/disassemble.cpp |
| // but with no public header. |
| // This is a C++ function, so the name is mangled, and signature changes will |
| // result in a linker error instead of runtime signature mismatches. |
| extern std::string spvInstructionBinaryToText(const spv_target_env env, |
| const uint32_t *inst_binary, |
| const size_t inst_word_count, |
| const uint32_t *binary, |
| const size_t word_count, |
| const uint32_t options); |
| |
| } // namespace spvtools |
| |
| namespace { |
| |
| const char *laneNames[] = { "Lane 0", "Lane 1", "Lane 2", "Lane 3" }; |
| static_assert(sizeof(laneNames) / sizeof(laneNames[0]) == sw::SIMD::Width, |
| "laneNames must have SIMD::Width entries"); |
| |
| template<typename T> |
| std::string tostring(const T &s) |
| { |
| return std::to_string(s); |
| } |
| std::string tostring(const char *s) |
| { |
| return s; |
| } |
| std::string tostring(sw::SpirvShader::Object::ID id) |
| { |
| return "%" + std::to_string(id.value()); |
| } |
| |
| } // anonymous namespace |
| |
| namespace rr { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // rr::CToReactor<T> specializations. |
| //////////////////////////////////////////////////////////////////////////////// |
| template<typename T> |
| struct CToReactor<sw::SpirvID<T>> |
| { |
| using type = rr::Int; |
| static rr::Int cast(sw::SpirvID<T> id) { return rr::Int(id.value()); } |
| }; |
| |
| template<typename T> |
| struct CToReactor<vk::dbg::ID<T>> |
| { |
| using type = rr::Int; |
| static rr::Int cast(vk::dbg::ID<T> id) { return rr::Int(id.value()); } |
| }; |
| |
| } // namespace rr |
| |
| namespace sw { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // sw::SpirvShader::Impl::Debugger |
| // |
| // Private struct holding debugger information for the SpirvShader. |
| //////////////////////////////////////////////////////////////////////////////// |
| struct SpirvShader::Impl::Debugger |
| { |
| class Group; |
| class State; |
| |
| void setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column); |
| |
| // exposeVariable exposes the variable with the given ID to the debugger |
| // using the specified key. |
| template<typename Key> |
| void exposeVariable( |
| const SpirvShader *shader, |
| const Key &key, |
| Object::ID id, |
| EmitState *state) const; |
| |
| // exposeVariable exposes the variable with the given ID to the |
| // debugger under the specified group, for the specified SIMD lane. |
| template<typename Key> |
| void exposeVariable( |
| const SpirvShader *shader, |
| const Group &group, |
| int lane, |
| const Key &key, |
| Object::ID id, |
| EmitState *state, |
| int wordOffset = 0) const; |
| |
| std::shared_ptr<vk::dbg::Context> ctx; |
| std::shared_ptr<vk::dbg::File> spirvFile; |
| std::unordered_map<const void *, int> spirvLineMappings; // instruction pointer to line |
| std::unordered_map<const void *, Object::ID> results; // instruction pointer to result ID |
| |
| private: |
| std::unordered_map<std::string, vk::dbg::File::ID> fileIDs; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // sw::SpirvShader::Impl::Debugger::State |
| // |
| // State holds the runtime data structures for the shader debug session. |
| //////////////////////////////////////////////////////////////////////////////// |
| class SpirvShader::Impl::Debugger::State |
| { |
| public: |
| static State *create(const Debugger *debugger, const char *name); |
| static void destroy(State *); |
| |
| State(const Debugger *debugger, const char *stackBase, vk::dbg::Context::Lock &lock); |
| ~State(); |
| |
| void enter(vk::dbg::Context::Lock &lock, const char *name); |
| void exit(); |
| void updateActiveLaneMask(int lane, bool enabled); |
| void update(vk::dbg::File::ID file, int line, int column); |
| |
| vk::dbg::VariableContainer *locals(); |
| vk::dbg::VariableContainer *hovers(); |
| vk::dbg::VariableContainer *localsLane(int lane); |
| |
| template<typename K> |
| vk::dbg::VariableContainer *group(vk::dbg::VariableContainer *vc, K key); |
| |
| template<typename K, typename V> |
| void putVal(vk::dbg::VariableContainer *vc, K key, V value); |
| |
| struct Scopes |
| { |
| std::shared_ptr<vk::dbg::Scope> locals; |
| std::shared_ptr<vk::dbg::Scope> hovers; |
| std::array<std::shared_ptr<vk::dbg::VariableContainer>, sw::SIMD::Width> localsByLane; |
| }; |
| |
| const Debugger *debugger; |
| const std::shared_ptr<vk::dbg::Thread> thread; |
| Scopes threadScopes; |
| }; |
| |
| SpirvShader::Impl::Debugger::State *SpirvShader::Impl::Debugger::State::create(const Debugger *debugger, const char *name) |
| { |
| auto lock = debugger->ctx->lock(); |
| return new State(debugger, name, lock); |
| } |
| |
| void SpirvShader::Impl::Debugger::State::destroy(State *state) |
| { |
| delete state; |
| } |
| |
| SpirvShader::Impl::Debugger::State::State(const Debugger *debugger, const char *stackBase, vk::dbg::Context::Lock &lock) |
| : debugger(debugger) |
| , thread(lock.currentThread()) |
| { |
| enter(lock, stackBase); |
| thread->update([&](vk::dbg::Frame &frame) { |
| threadScopes.locals = frame.locals; |
| threadScopes.hovers = frame.hovers; |
| for(int i = 0; i < sw::SIMD::Width; i++) |
| { |
| threadScopes.localsByLane[i] = lock.createVariableContainer(); |
| frame.locals->variables->put(laneNames[i], threadScopes.localsByLane[i]); |
| } |
| }); |
| } |
| |
| SpirvShader::Impl::Debugger::State::~State() |
| { |
| exit(); |
| } |
| |
| void SpirvShader::Impl::Debugger::State::enter(vk::dbg::Context::Lock &lock, const char *name) |
| { |
| thread->enter(lock, debugger->spirvFile, name); |
| } |
| |
| void SpirvShader::Impl::Debugger::State::exit() |
| { |
| thread->exit(); |
| } |
| |
| void SpirvShader::Impl::Debugger::State::updateActiveLaneMask(int lane, bool enabled) |
| { |
| threadScopes.localsByLane[lane]->put("enabled", vk::dbg::make_constant(enabled)); |
| } |
| |
| void SpirvShader::Impl::Debugger::State::update(vk::dbg::File::ID fileID, int line, int column) |
| { |
| auto file = debugger->ctx->lock().get(fileID); |
| thread->update([&](vk::dbg::Frame &frame) { |
| frame.location = { file, line, column }; |
| }); |
| } |
| |
| vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::locals() |
| { |
| return threadScopes.locals->variables.get(); |
| } |
| |
| vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::hovers() |
| { |
| return threadScopes.hovers->variables.get(); |
| } |
| |
| vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::localsLane(int i) |
| { |
| return threadScopes.localsByLane[i].get(); |
| } |
| |
| template<typename K> |
| vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::group(vk::dbg::VariableContainer *vc, K key) |
| { |
| auto out = debugger->ctx->lock().createVariableContainer(); |
| vc->put(tostring(key), out); |
| return out.get(); |
| } |
| |
| template<typename K, typename V> |
| void SpirvShader::Impl::Debugger::State::putVal(vk::dbg::VariableContainer *vc, K key, V value) |
| { |
| vc->put(tostring(key), vk::dbg::make_constant(value)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // sw::SpirvShader::Impl::Debugger::Group |
| // |
| // This provides a convenient C++ interface for adding debugger values to |
| // VariableContainers. |
| //////////////////////////////////////////////////////////////////////////////// |
| class SpirvShader::Impl::Debugger::Group |
| { |
| public: |
| using Ptr = rr::Pointer<rr::Byte>; |
| |
| static Group hovers(Ptr state); |
| static Group locals(Ptr state); |
| static Group localsLane(Ptr state, int lane); |
| |
| Group(Ptr state, Ptr group); |
| |
| template<typename K, typename RK> |
| Group group(RK key) const; |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void put(RK key, RV value) const; |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void put(RK key, RV x, RV y) const; |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void put(RK key, RV x, RV y, RV z) const; |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void put(RK key, RV x, RV y, RV z, RV w) const; |
| |
| template<typename K, typename V, typename VEC> |
| void putVec3(K key, const VEC &v) const; |
| |
| private: |
| Ptr state; |
| Ptr ptr; |
| }; |
| |
| SpirvShader::Impl::Debugger::Group |
| SpirvShader::Impl::Debugger::Group::hovers(Ptr state) |
| { |
| return Group(state, rr::Call(&State::hovers, state)); |
| } |
| |
| SpirvShader::Impl::Debugger::Group |
| SpirvShader::Impl::Debugger::Group::locals(Ptr state) |
| { |
| return Group(state, rr::Call(&State::locals, state)); |
| } |
| |
| SpirvShader::Impl::Debugger::Group |
| SpirvShader::Impl::Debugger::Group::localsLane(Ptr state, int lane) |
| { |
| return Group(state, rr::Call(&State::localsLane, state, lane)); |
| } |
| |
| SpirvShader::Impl::Debugger::Group::Group(Ptr state, Ptr group) |
| : state(state) |
| , ptr(group) |
| {} |
| |
| template<typename K, typename RK> |
| SpirvShader::Impl::Debugger::Group SpirvShader::Impl::Debugger::Group::group(RK key) const |
| { |
| return Group(state, rr::Call(&State::group<K>, state, ptr, key)); |
| } |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void SpirvShader::Impl::Debugger::Group::put(RK key, RV value) const |
| { |
| rr::Call(&State::putVal<K, V>, state, ptr, key, value); |
| } |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void SpirvShader::Impl::Debugger::Group::put(RK key, RV x, RV y) const |
| { |
| auto vec = group<K>(key); |
| vec.template put<const char *, V>("x", x); |
| vec.template put<const char *, V>("y", y); |
| } |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void SpirvShader::Impl::Debugger::Group::put(RK key, RV x, RV y, RV z) const |
| { |
| auto vec = group<K>(key); |
| vec.template put<const char *, V>("x", x); |
| vec.template put<const char *, V>("y", y); |
| vec.template put<const char *, V>("z", z); |
| } |
| |
| template<typename K, typename V, typename RK, typename RV> |
| void SpirvShader::Impl::Debugger::Group::put(RK key, RV x, RV y, RV z, RV w) const |
| { |
| auto vec = group<K>(key); |
| vec.template put<const char *, V>("x", x); |
| vec.template put<const char *, V>("y", y); |
| vec.template put<const char *, V>("z", z); |
| vec.template put<const char *, V>("w", w); |
| } |
| |
| template<typename K, typename V, typename VEC> |
| void SpirvShader::Impl::Debugger::Group::putVec3(K key, const VEC &v) const |
| { |
| auto vec = group<K>(key); |
| vec.template put<const char *, V>("x", Extract(v, 0)); |
| vec.template put<const char *, V>("y", Extract(v, 1)); |
| vec.template put<const char *, V>("z", Extract(v, 2)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // sw::SpirvShader::Impl::Debugger methods |
| //////////////////////////////////////////////////////////////////////////////// |
| void SpirvShader::Impl::Debugger::setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column) |
| { |
| auto it = fileIDs.find(path); |
| if(it != fileIDs.end()) |
| { |
| rr::Call(&State::update, state->routine->dbgState, it->second, line, column); |
| } |
| } |
| |
| template<typename Key> |
| void SpirvShader::Impl::Debugger::exposeVariable( |
| const SpirvShader *shader, |
| const Key &key, |
| Object::ID id, |
| EmitState *state) const |
| { |
| auto dbgState = state->routine->dbgState; |
| auto hover = Group::hovers(dbgState).group<Key>(key); |
| for(int lane = 0; lane < SIMD::Width; lane++) |
| { |
| exposeVariable(shader, Group::localsLane(dbgState, lane), lane, key, id, state); |
| exposeVariable(shader, hover, lane, laneNames[lane], id, state); |
| } |
| } |
| |
| template<typename Key> |
| void SpirvShader::Impl::Debugger::exposeVariable( |
| const SpirvShader *shader, |
| const Group &group, |
| int l, |
| const Key &key, |
| Object::ID id, |
| EmitState *state, |
| int wordOffset /* = 0 */) const |
| { |
| GenericValue val(shader, state, id); |
| switch(shader->getType(val.type).opcode()) |
| { |
| case spv::OpTypeInt: |
| { |
| group.put<Key, int>(key, Extract(val.Int(0), l)); |
| } |
| break; |
| case spv::OpTypeFloat: |
| { |
| group.put<Key, float>(key, Extract(val.Float(0), l)); |
| } |
| break; |
| case spv::OpTypeVector: |
| { |
| auto count = shader->getType(val.type).definition.word(3); |
| switch(count) |
| { |
| case 1: |
| group.put<Key, float>(key, Extract(val.Float(0), l)); |
| break; |
| case 2: |
| group.put<Key, float>(key, Extract(val.Float(0), l), Extract(val.Float(1), l)); |
| break; |
| case 3: |
| group.put<Key, float>(key, Extract(val.Float(0), l), Extract(val.Float(1), l), Extract(val.Float(2), l)); |
| break; |
| case 4: |
| group.put<Key, float>(key, Extract(val.Float(0), l), Extract(val.Float(1), l), Extract(val.Float(2), l), Extract(val.Float(3), l)); |
| break; |
| default: |
| { |
| auto vec = group.group<Key>(key); |
| for(uint32_t i = 0; i < count; i++) |
| { |
| vec.template put<int, float>(i, Extract(val.Float(i), l)); |
| } |
| } |
| break; |
| } |
| } |
| break; |
| case spv::OpTypePointer: |
| { |
| auto objectTy = shader->getType(shader->getObject(id).type); |
| bool interleavedByLane = IsStorageInterleavedByLane(objectTy.storageClass); |
| auto ptr = state->getPointer(id); |
| auto ptrGroup = group.group<Key>(key); |
| shader->VisitMemoryObject(id, [&](const MemoryElement &el) { |
| auto p = ptr + el.offset; |
| if(interleavedByLane) { p = InterleaveByLane(p); } // TODO: Interleave once, then add offset? |
| auto simd = p.Load<SIMD::Float>(sw::OutOfBoundsBehavior::Nullify, state->activeLaneMask()); |
| ptrGroup.template put<int, float>(el.index, Extract(simd, l)); |
| }); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // sw::SpirvShader |
| //////////////////////////////////////////////////////////////////////////////// |
| void SpirvShader::dbgInit(const std::shared_ptr<vk::dbg::Context> &dbgctx) |
| { |
| impl.debugger = new Impl::Debugger(); |
| impl.debugger->ctx = dbgctx; |
| } |
| |
| void SpirvShader::dbgTerm() |
| { |
| if(impl.debugger) |
| { |
| delete impl.debugger; |
| } |
| } |
| |
| void SpirvShader::dbgCreateFile() |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| int currentLine = 1; |
| std::string source; |
| for(auto insn : *this) |
| { |
| auto instruction = spvtools::spvInstructionBinaryToText( |
| SPV_ENV_VULKAN_1_1, |
| insn.wordPointer(0), |
| insn.wordCount(), |
| insns.data(), |
| insns.size(), |
| SPV_BINARY_TO_TEXT_OPTION_NO_HEADER) + |
| "\n"; |
| dbg->spirvLineMappings[insn.wordPointer(0)] = currentLine; |
| currentLine += std::count(instruction.begin(), instruction.end(), '\n'); |
| source += instruction; |
| } |
| std::string name; |
| switch(executionModel) |
| { |
| case spv::ExecutionModelVertex: name = "VertexShader"; break; |
| case spv::ExecutionModelFragment: name = "FragmentShader"; break; |
| case spv::ExecutionModelGLCompute: name = "ComputeShader"; break; |
| default: name = "SPIR-V Shader"; break; |
| } |
| static std::atomic<int> id = { 0 }; |
| name += std::to_string(id++) + ".spvasm"; |
| dbg->spirvFile = dbg->ctx->lock().createVirtualFile(name.c_str(), source.c_str()); |
| } |
| |
| void SpirvShader::dbgBeginEmit(EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| using Group = Impl::Debugger::Group; |
| |
| auto routine = state->routine; |
| |
| auto type = "SPIR-V"; |
| switch(executionModel) |
| { |
| case spv::ExecutionModelVertex: type = "VertexShader"; break; |
| case spv::ExecutionModelFragment: type = "FragmentShader"; break; |
| case spv::ExecutionModelGLCompute: type = "ComputeShader"; break; |
| default: type = "SPIR-V Shader"; break; |
| } |
| auto dbgState = rr::Call(&Impl::Debugger::State::create, dbg, type); |
| |
| routine->dbgState = dbgState; |
| |
| SetActiveLaneMask(state->activeLaneMask(), state); |
| |
| auto locals = Group::locals(dbgState); |
| locals.put<const char *, int>("subgroupSize", routine->invocationsPerSubgroup); |
| |
| switch(executionModel) |
| { |
| case spv::ExecutionModelGLCompute: |
| locals.putVec3<const char *, int>("numWorkgroups", routine->numWorkgroups); |
| locals.putVec3<const char *, int>("workgroupID", routine->workgroupID); |
| locals.putVec3<const char *, int>("workgroupSize", routine->workgroupSize); |
| locals.put<const char *, int>("numSubgroups", routine->subgroupsPerWorkgroup); |
| locals.put<const char *, int>("subgroupIndex", routine->subgroupIndex); |
| |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| auto lane = Group::localsLane(dbgState, i); |
| lane.put<const char *, int>("globalInvocationId", |
| rr::Extract(routine->globalInvocationID[0], i), |
| rr::Extract(routine->globalInvocationID[1], i), |
| rr::Extract(routine->globalInvocationID[2], i)); |
| lane.put<const char *, int>("localInvocationId", |
| rr::Extract(routine->localInvocationID[0], i), |
| rr::Extract(routine->localInvocationID[1], i), |
| rr::Extract(routine->localInvocationID[2], i)); |
| lane.put<const char *, int>("localInvocationIndex", rr::Extract(routine->localInvocationIndex, i)); |
| } |
| break; |
| |
| case spv::ExecutionModelFragment: |
| locals.put<const char *, int>("viewIndex", routine->viewID); |
| for(int i = 0; i < SIMD::Width; i++) |
| { |
| auto lane = Group::localsLane(dbgState, i); |
| lane.put<const char *, float>("fragCoord", |
| rr::Extract(routine->fragCoord[0], i), |
| rr::Extract(routine->fragCoord[1], i), |
| rr::Extract(routine->fragCoord[2], i), |
| rr::Extract(routine->fragCoord[3], i)); |
| lane.put<const char *, float>("pointCoord", |
| rr::Extract(routine->pointCoord[0], i), |
| rr::Extract(routine->pointCoord[1], i)); |
| lane.put<const char *, int>("windowSpacePosition", |
| rr::Extract(routine->windowSpacePosition[0], i), |
| rr::Extract(routine->windowSpacePosition[1], i)); |
| lane.put<const char *, int>("helperInvocation", rr::Extract(routine->helperInvocation, i)); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| void SpirvShader::dbgEndEmit(EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| rr::Call(&Impl::Debugger::State::destroy, state->routine->dbgState); |
| } |
| |
| void SpirvShader::dbgBeginEmitInstruction(InsnIterator insn, EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| auto line = dbg->spirvLineMappings.at(insn.wordPointer(0)); |
| auto column = 0; |
| rr::Call(&Impl::Debugger::State::update, state->routine->dbgState, dbg->spirvFile->id, line, column); |
| } |
| |
| void SpirvShader::dbgEndEmitInstruction(InsnIterator insn, EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| auto resIt = dbg->results.find(insn.wordPointer(0)); |
| if(resIt != dbg->results.end()) |
| { |
| auto id = resIt->second; |
| dbgExposeIntermediate(id, state); |
| } |
| } |
| |
| void SpirvShader::dbgExposeIntermediate(Object::ID id, EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| dbg->exposeVariable(this, id, id, state); |
| } |
| |
| void SpirvShader::dbgUpdateActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| for(int lane = 0; lane < SIMD::Width; lane++) |
| { |
| rr::Call(&Impl::Debugger::State::updateActiveLaneMask, state->routine->dbgState, lane, rr::Extract(mask, lane) != 0); |
| } |
| } |
| |
| void SpirvShader::dbgDeclareResult(const InsnIterator &insn, Object::ID resultId) const |
| { |
| auto dbg = impl.debugger; |
| if(!dbg) { return; } |
| |
| dbg->results.emplace(insn.wordPointer(0), resultId); |
| } |
| |
| SpirvShader::EmitResult SpirvShader::EmitLine(InsnIterator insn, EmitState *state) const |
| { |
| if(auto dbg = impl.debugger) |
| { |
| auto path = getString(insn.word(1)); |
| auto line = insn.word(2); |
| auto column = insn.word(3); |
| dbg->setPosition(state, path, line, column); |
| } |
| return EmitResult::Continue; |
| } |
| |
| } // namespace sw |
| |
| #else // ENABLE_VK_DEBUGGER |
| |
| // Stub implementations of the dbgXXX functions. |
| namespace sw { |
| |
| void SpirvShader::dbgInit(const std::shared_ptr<vk::dbg::Context> &dbgctx) {} |
| void SpirvShader::dbgTerm() {} |
| void SpirvShader::dbgCreateFile() {} |
| void SpirvShader::dbgBeginEmit(EmitState *state) const {} |
| void SpirvShader::dbgEndEmit(EmitState *state) const {} |
| void SpirvShader::dbgBeginEmitInstruction(InsnIterator insn, EmitState *state) const {} |
| void SpirvShader::dbgEndEmitInstruction(InsnIterator insn, EmitState *state) const {} |
| void SpirvShader::dbgExposeIntermediate(Object::ID id, EmitState *state) const {} |
| void SpirvShader::dbgUpdateActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const {} |
| void SpirvShader::dbgDeclareResult(const InsnIterator &insn, Object::ID resultId) const {} |
| |
| SpirvShader::EmitResult SpirvShader::EmitLine(InsnIterator insn, EmitState *state) const |
| { |
| return EmitResult::Continue; |
| } |
| |
| } // namespace sw |
| |
| #endif // ENABLE_VK_DEBUGGER |