<commit_before>// Copyright 2017 The NXT Authors
//
// 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 "backend/vulkan/CommandBufferVk.h"

#include "backend/Commands.h"
#include "backend/vulkan/BindGroupVk.h"
#include "backend/vulkan/BufferVk.h"
#include "backend/vulkan/FramebufferVk.h"
#include "backend/vulkan/PipelineLayoutVk.h"
#include "backend/vulkan/RenderPassVk.h"
#include "backend/vulkan/RenderPipelineVk.h"
#include "backend/vulkan/TextureVk.h"
#include "backend/vulkan/VulkanBackend.h"

namespace backend { namespace vulkan {

    namespace {

        VkBufferImageCopy ComputeBufferImageCopyRegion(uint32_t rowPitch,
                                                       const BufferCopyLocation& bufferLocation,
                                                       const TextureCopyLocation& textureLocation) {
            const Texture* texture = ToBackend(textureLocation.texture).Get();

            VkBufferImageCopy region;

            region.bufferOffset = bufferLocation.offset;
            // In Vulkan the row length is in texels while it is in bytes for NXT
            region.bufferRowLength = rowPitch / TextureFormatPixelSize(texture->GetFormat());
            region.bufferImageHeight = rowPitch * textureLocation.height;

            region.imageSubresource.aspectMask = texture->GetVkAspectMask();
            region.imageSubresource.mipLevel = textureLocation.level;
            region.imageSubresource.baseArrayLayer = 0;
            region.imageSubresource.layerCount = 1;

            region.imageOffset.x = textureLocation.x;
            region.imageOffset.y = textureLocation.y;
            region.imageOffset.z = textureLocation.z;

            region.imageExtent.width = textureLocation.width;
            region.imageExtent.height = textureLocation.height;
            region.imageExtent.depth = textureLocation.depth;

            return region;
        }

    }  // anonymous namespace

    CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
    }

    CommandBuffer::~CommandBuffer() {
        FreeCommands(&mCommands);
    }

    void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
        Device* device = ToBackend(GetDevice());

        RenderPipeline* lastRenderPipeline = nullptr;

        Command type;
        while (mCommands.NextCommandId(&type)) {
            switch (type) {
                case Command::CopyBufferToBuffer: {
                    CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBufferCopy region;
                    region.srcOffset = src.offset;
                    region.dstOffset = dst.offset;
                    region.size = copy->size;

                    VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle();
                    VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle();
                    device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, &region);
                } break;

                case Command::CopyBufferToTexture: {
                    CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
                    VkImage dstImage = ToBackend(dst.texture)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, src, dst);

                    // The image is written to so the NXT guarantees make sure it is in the
                    // TRANSFER_DST_OPTIMAL layout
                    device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage,
                                                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
                                                    &region);
                } break;

                case Command::CopyTextureToBuffer: {
                    CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkImage srcImage = ToBackend(src.texture)->GetHandle();
                    VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, dst, src);

                    // The NXT TransferSrc usage is always mapped to GENERAL
                    device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL,
                                                    dstBuffer, 1, &region);
                } break;

                case Command::BeginRenderPass: {
                    BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
                    Framebuffer* framebuffer = ToBackend(cmd->framebuffer.Get());
                    RenderPass* renderPass = ToBackend(cmd->renderPass.Get());

                    // NXT has an implicit transition to color attachment on subpasses. Transition
                    // the attachments now before we start the render pass.
                    for (uint32_t i = 0; i < renderPass->GetAttachmentCount(); ++i) {
                        Texture* attachment =
                            ToBackend(framebuffer->GetTextureView(i)->GetTexture());

                        if (attachment->GetUsage() & nxt::TextureUsageBit::OutputAttachment) {
                            continue;
                        }

                        attachment->RecordBarrier(commands, attachment->GetUsage(),
                                                  nxt::TextureUsageBit::OutputAttachment);
                        attachment->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment);
                    }

                    ASSERT(renderPass->GetSubpassCount() == 1);
                    ASSERT(renderPass->GetAttachmentCount() <= kMaxColorAttachments + 1);

                    std::array<VkClearValue, kMaxColorAttachments + 1> clearValues;
                    framebuffer->FillClearValues(clearValues.data());

                    VkRenderPassBeginInfo beginInfo;
                    beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                    beginInfo.pNext = nullptr;
                    beginInfo.renderPass = renderPass->GetHandle();
                    beginInfo.framebuffer = framebuffer->GetHandle();
                    beginInfo.renderArea.offset.x = 0;
                    beginInfo.renderArea.offset.y = 0;
                    beginInfo.renderArea.extent.width = framebuffer->GetWidth();
                    beginInfo.renderArea.extent.height = framebuffer->GetHeight();
                    beginInfo.clearValueCount = renderPass->GetAttachmentCount();
                    beginInfo.pClearValues = clearValues.data();

                    device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);

                    // Set all the dynamic state just in case.
                    device->fn.CmdSetLineWidth(commands, 1.0f);
                    device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f);

                    device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0);

                    // The viewport and scissor default to cover all of the attachments
                    VkViewport viewport;
                    viewport.x = 0.0f;
                    viewport.y = 0.0f;
                    viewport.width = static_cast<float>(framebuffer->GetWidth());
                    viewport.height = static_cast<float>(framebuffer->GetHeight());
                    viewport.minDepth = 0.0f;
                    viewport.maxDepth = 1.0f;
                    device->fn.CmdSetViewport(commands, 0, 1, &viewport);

                    VkRect2D scissorRect;
                    scissorRect.offset.x = 0;
                    scissorRect.offset.y = 0;
                    scissorRect.extent.width = framebuffer->GetWidth();
                    scissorRect.extent.height = framebuffer->GetHeight();
                    device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
                } break;

                case Command::BeginRenderSubpass: {
                    mCommands.NextCommand<BeginRenderSubpassCmd>();
                    // Do nothing related to subpasses because the single subpass is started in
                    // vkBeginRenderPass

                    // Set up the default state
                    float blendConstants[4] = {
                        0.0f,
                        0.0f,
                        0.0f,
                        0.0f,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::DrawArrays: {
                    DrawArraysCmd* draw = mCommands.NextCommand<DrawArraysCmd>();

                    device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
                                       draw->firstVertex, draw->firstInstance);
                } break;

                case Command::DrawElements: {
                    DrawElementsCmd* draw = mCommands.NextCommand<DrawElementsCmd>();

                    uint32_t vertexOffset = 0;
                    device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
                                              draw->firstIndex, vertexOffset, draw->firstInstance);
                } break;

                case Command::EndRenderPass: {
                    mCommands.NextCommand<EndRenderPassCmd>();
                    device->fn.CmdEndRenderPass(commands);
                } break;

                case Command::EndRenderSubpass: {
                    mCommands.NextCommand<EndRenderSubpassCmd>();
                    // Do nothing because the single subpass is ended in vkEndRenderPass
                } break;

                case Command::SetBindGroup: {
                    SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
                    VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();

                    // TODO(cwallez@chromium.org): Add some dirty bits for this to allow setting
                    // before there is a pipeline layout
                    // TODO(cwallez@chromium.org): fix for compute passes
                    VkPipelineLayout layout =
                        ToBackend(lastRenderPipeline->GetLayout())->GetHandle();
                    device->fn.CmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                                     layout, cmd->index, 1, &set, 0, nullptr);
                } break;

                case Command::SetBlendColor: {
                    SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
                    float blendConstants[4] = {
                        cmd->r,
                        cmd->g,
                        cmd->b,
                        cmd->a,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::SetIndexBuffer: {
                    SetIndexBufferCmd* cmd = mCommands.NextCommand<SetIndexBufferCmd>();
                    VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle();

                    // TODO(cwallez@chromium.org): get the index type from the last render pipeline
                    // and rebind if needed on pipeline change
                    device->fn.CmdBindIndexBuffer(commands, indexBuffer,
                                                  static_cast<VkDeviceSize>(cmd->offset),
                                                  VK_INDEX_TYPE_UINT16);
                } break;

                case Command::SetRenderPipeline: {
                    SetRenderPipelineCmd* cmd = mCommands.NextCommand<SetRenderPipelineCmd>();
                    RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();

                    device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                               pipeline->GetHandle());
                    lastRenderPipeline = pipeline;
                } break;

                case Command::SetVertexBuffers: {
                    SetVertexBuffersCmd* cmd = mCommands.NextCommand<SetVertexBuffersCmd>();
                    auto buffers = mCommands.NextData<Ref<BufferBase>>(cmd->count);
                    auto offsets = mCommands.NextData<uint32_t>(cmd->count);

                    std::array<VkBuffer, kMaxVertexInputs> vkBuffers;
                    std::array<VkDeviceSize, kMaxVertexInputs> vkOffsets;

                    for (uint32_t i = 0; i < cmd->count; ++i) {
                        Buffer* buffer = ToBackend(buffers[i].Get());
                        vkBuffers[i] = buffer->GetHandle();
                        vkOffsets[i] = static_cast<VkDeviceSize>(offsets[i]);
                    }

                    device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count,
                                                    vkBuffers.data(), vkOffsets.data());
                } break;

                case Command::TransitionBufferUsage: {
                    TransitionBufferUsageCmd* cmd =
                        mCommands.NextCommand<TransitionBufferUsageCmd>();

                    Buffer* buffer = ToBackend(cmd->buffer.Get());
                    buffer->RecordBarrier(commands, buffer->GetUsage(), cmd->usage);
                    buffer->UpdateUsageInternal(cmd->usage);
                } break;

                case Command::TransitionTextureUsage: {
                    TransitionTextureUsageCmd* cmd =
                        mCommands.NextCommand<TransitionTextureUsageCmd>();

                    Texture* texture = ToBackend(cmd->texture.Get());
                    texture->RecordBarrier(commands, texture->GetUsage(), cmd->usage);
                    texture->UpdateUsageInternal(cmd->usage);
                } break;

                default: { UNREACHABLE(); } break;
            }
        }
    }

}}  // namespace backend::vulkan
<commit_msg>Vulkan: Implement SetStencilReference<commit_after>// Copyright 2017 The NXT Authors
//
// 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 "backend/vulkan/CommandBufferVk.h"

#include "backend/Commands.h"
#include "backend/vulkan/BindGroupVk.h"
#include "backend/vulkan/BufferVk.h"
#include "backend/vulkan/FramebufferVk.h"
#include "backend/vulkan/PipelineLayoutVk.h"
#include "backend/vulkan/RenderPassVk.h"
#include "backend/vulkan/RenderPipelineVk.h"
#include "backend/vulkan/TextureVk.h"
#include "backend/vulkan/VulkanBackend.h"

namespace backend { namespace vulkan {

    namespace {

        VkBufferImageCopy ComputeBufferImageCopyRegion(uint32_t rowPitch,
                                                       const BufferCopyLocation& bufferLocation,
                                                       const TextureCopyLocation& textureLocation) {
            const Texture* texture = ToBackend(textureLocation.texture).Get();

            VkBufferImageCopy region;

            region.bufferOffset = bufferLocation.offset;
            // In Vulkan the row length is in texels while it is in bytes for NXT
            region.bufferRowLength = rowPitch / TextureFormatPixelSize(texture->GetFormat());
            region.bufferImageHeight = rowPitch * textureLocation.height;

            region.imageSubresource.aspectMask = texture->GetVkAspectMask();
            region.imageSubresource.mipLevel = textureLocation.level;
            region.imageSubresource.baseArrayLayer = 0;
            region.imageSubresource.layerCount = 1;

            region.imageOffset.x = textureLocation.x;
            region.imageOffset.y = textureLocation.y;
            region.imageOffset.z = textureLocation.z;

            region.imageExtent.width = textureLocation.width;
            region.imageExtent.height = textureLocation.height;
            region.imageExtent.depth = textureLocation.depth;

            return region;
        }

    }  // anonymous namespace

    CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
    }

    CommandBuffer::~CommandBuffer() {
        FreeCommands(&mCommands);
    }

    void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
        Device* device = ToBackend(GetDevice());

        RenderPipeline* lastRenderPipeline = nullptr;

        Command type;
        while (mCommands.NextCommandId(&type)) {
            switch (type) {
                case Command::CopyBufferToBuffer: {
                    CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBufferCopy region;
                    region.srcOffset = src.offset;
                    region.dstOffset = dst.offset;
                    region.size = copy->size;

                    VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle();
                    VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle();
                    device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, &region);
                } break;

                case Command::CopyBufferToTexture: {
                    CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
                    VkImage dstImage = ToBackend(dst.texture)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, src, dst);

                    // The image is written to so the NXT guarantees make sure it is in the
                    // TRANSFER_DST_OPTIMAL layout
                    device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage,
                                                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
                                                    &region);
                } break;

                case Command::CopyTextureToBuffer: {
                    CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkImage srcImage = ToBackend(src.texture)->GetHandle();
                    VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, dst, src);

                    // The NXT TransferSrc usage is always mapped to GENERAL
                    device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL,
                                                    dstBuffer, 1, &region);
                } break;

                case Command::BeginRenderPass: {
                    BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
                    Framebuffer* framebuffer = ToBackend(cmd->framebuffer.Get());
                    RenderPass* renderPass = ToBackend(cmd->renderPass.Get());

                    // NXT has an implicit transition to color attachment on subpasses. Transition
                    // the attachments now before we start the render pass.
                    for (uint32_t i = 0; i < renderPass->GetAttachmentCount(); ++i) {
                        Texture* attachment =
                            ToBackend(framebuffer->GetTextureView(i)->GetTexture());

                        if (attachment->GetUsage() & nxt::TextureUsageBit::OutputAttachment) {
                            continue;
                        }

                        attachment->RecordBarrier(commands, attachment->GetUsage(),
                                                  nxt::TextureUsageBit::OutputAttachment);
                        attachment->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment);
                    }

                    ASSERT(renderPass->GetSubpassCount() == 1);
                    ASSERT(renderPass->GetAttachmentCount() <= kMaxColorAttachments + 1);

                    std::array<VkClearValue, kMaxColorAttachments + 1> clearValues;
                    framebuffer->FillClearValues(clearValues.data());

                    VkRenderPassBeginInfo beginInfo;
                    beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                    beginInfo.pNext = nullptr;
                    beginInfo.renderPass = renderPass->GetHandle();
                    beginInfo.framebuffer = framebuffer->GetHandle();
                    beginInfo.renderArea.offset.x = 0;
                    beginInfo.renderArea.offset.y = 0;
                    beginInfo.renderArea.extent.width = framebuffer->GetWidth();
                    beginInfo.renderArea.extent.height = framebuffer->GetHeight();
                    beginInfo.clearValueCount = renderPass->GetAttachmentCount();
                    beginInfo.pClearValues = clearValues.data();

                    device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);

                    // Set all the dynamic state just in case.
                    device->fn.CmdSetLineWidth(commands, 1.0f);
                    device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f);

                    device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0);

                    // The viewport and scissor default to cover all of the attachments
                    VkViewport viewport;
                    viewport.x = 0.0f;
                    viewport.y = 0.0f;
                    viewport.width = static_cast<float>(framebuffer->GetWidth());
                    viewport.height = static_cast<float>(framebuffer->GetHeight());
                    viewport.minDepth = 0.0f;
                    viewport.maxDepth = 1.0f;
                    device->fn.CmdSetViewport(commands, 0, 1, &viewport);

                    VkRect2D scissorRect;
                    scissorRect.offset.x = 0;
                    scissorRect.offset.y = 0;
                    scissorRect.extent.width = framebuffer->GetWidth();
                    scissorRect.extent.height = framebuffer->GetHeight();
                    device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
                } break;

                case Command::BeginRenderSubpass: {
                    mCommands.NextCommand<BeginRenderSubpassCmd>();
                    // Do nothing related to subpasses because the single subpass is started in
                    // vkBeginRenderPass

                    // Set up the default state
                    float blendConstants[4] = {
                        0.0f,
                        0.0f,
                        0.0f,
                        0.0f,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::DrawArrays: {
                    DrawArraysCmd* draw = mCommands.NextCommand<DrawArraysCmd>();

                    device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
                                       draw->firstVertex, draw->firstInstance);
                } break;

                case Command::DrawElements: {
                    DrawElementsCmd* draw = mCommands.NextCommand<DrawElementsCmd>();

                    uint32_t vertexOffset = 0;
                    device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
                                              draw->firstIndex, vertexOffset, draw->firstInstance);
                } break;

                case Command::EndRenderPass: {
                    mCommands.NextCommand<EndRenderPassCmd>();
                    device->fn.CmdEndRenderPass(commands);
                } break;

                case Command::EndRenderSubpass: {
                    mCommands.NextCommand<EndRenderSubpassCmd>();
                    // Do nothing because the single subpass is ended in vkEndRenderPass
                } break;

                case Command::SetBindGroup: {
                    SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
                    VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();

                    // TODO(cwallez@chromium.org): Add some dirty bits for this to allow setting
                    // before there is a pipeline layout
                    // TODO(cwallez@chromium.org): fix for compute passes
                    VkPipelineLayout layout =
                        ToBackend(lastRenderPipeline->GetLayout())->GetHandle();
                    device->fn.CmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                                     layout, cmd->index, 1, &set, 0, nullptr);
                } break;

                case Command::SetBlendColor: {
                    SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
                    float blendConstants[4] = {
                        cmd->r,
                        cmd->g,
                        cmd->b,
                        cmd->a,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::SetIndexBuffer: {
                    SetIndexBufferCmd* cmd = mCommands.NextCommand<SetIndexBufferCmd>();
                    VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle();

                    // TODO(cwallez@chromium.org): get the index type from the last render pipeline
                    // and rebind if needed on pipeline change
                    device->fn.CmdBindIndexBuffer(commands, indexBuffer,
                                                  static_cast<VkDeviceSize>(cmd->offset),
                                                  VK_INDEX_TYPE_UINT16);
                } break;

                case Command::SetRenderPipeline: {
                    SetRenderPipelineCmd* cmd = mCommands.NextCommand<SetRenderPipelineCmd>();
                    RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();

                    device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                               pipeline->GetHandle());
                    lastRenderPipeline = pipeline;
                } break;

                case Command::SetStencilReference: {
                    SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>();
                    device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK,
                                                      cmd->reference);
                } break;

                case Command::SetVertexBuffers: {
                    SetVertexBuffersCmd* cmd = mCommands.NextCommand<SetVertexBuffersCmd>();
                    auto buffers = mCommands.NextData<Ref<BufferBase>>(cmd->count);
                    auto offsets = mCommands.NextData<uint32_t>(cmd->count);

                    std::array<VkBuffer, kMaxVertexInputs> vkBuffers;
                    std::array<VkDeviceSize, kMaxVertexInputs> vkOffsets;

                    for (uint32_t i = 0; i < cmd->count; ++i) {
                        Buffer* buffer = ToBackend(buffers[i].Get());
                        vkBuffers[i] = buffer->GetHandle();
                        vkOffsets[i] = static_cast<VkDeviceSize>(offsets[i]);
                    }

                    device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count,
                                                    vkBuffers.data(), vkOffsets.data());
                } break;

                case Command::TransitionBufferUsage: {
                    TransitionBufferUsageCmd* cmd =
                        mCommands.NextCommand<TransitionBufferUsageCmd>();

                    Buffer* buffer = ToBackend(cmd->buffer.Get());
                    buffer->RecordBarrier(commands, buffer->GetUsage(), cmd->usage);
                    buffer->UpdateUsageInternal(cmd->usage);
                } break;

                case Command::TransitionTextureUsage: {
                    TransitionTextureUsageCmd* cmd =
                        mCommands.NextCommand<TransitionTextureUsageCmd>();

                    Texture* texture = ToBackend(cmd->texture.Get());
                    texture->RecordBarrier(commands, texture->GetUsage(), cmd->usage);
                    texture->UpdateUsageInternal(cmd->usage);
                } break;

                default: { UNREACHABLE(); } break;
            }
        }
    }

}}  // namespace backend::vulkan
<|endoftext|>


<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/dbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <graph/degree_sort.hh>
#include <graph/min_width_sort.hh>
#include <threads/atomic_incumbent.hh>

#include <algorithm>
#include <thread>
#include <condition_variable>
#include <mutex>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct Job
    {
        std::mutex mutex;
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
    };

    template <unsigned size_>
    auto found_possible_new_best(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,
            const FixedBitSet<size_> & c,
            int c_popcount,
            const MaxCliqueParams & params,
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size);
        }
    }

    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    template <unsigned size_>
    auto expand_unstealable(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const MaxCliqueParams & params
            ) -> void
    {
        auto c_popcount = c.popcount();
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
            return;

        auto v = p_order[n];

        // consider taking v
        c.set(v);
        ++c_popcount;

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere);
        }
        else {
            // get our coloured vertices
            ++result.nodes;
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            expand_unstealable<size_>(graph, o, new_p_order, new_colours, c, new_p, result, best_anywhere, params);
        }

        // now consider not taking v
        c.unset(v);
        p.unset(v);
        if (n > 0) {
            expand_unstealable<size_>(graph, o, p_order, colours, c, p, result, best_anywhere, params);
        }
    }

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,
            Job<size_> * job,
            std::unique_lock<std::mutex> & job_guard,
            Job<size_> * child_job,
            std::mutex & stealing_mutex,
            std::condition_variable & stealing_cv,
            std::list<Job<size_> *> & steal_queue,
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const MaxCliqueParams & params
            ) -> bool
    {
        auto job_c_popcount = job->c.popcount();
        int n = job->p.popcount() - 1;

        // bound, timeout or early exit?
        if (-1 == n || bound(job_c_popcount, job->colours[n], params, best_anywhere) || params.abort.load())
            return false;

        auto v = job->p_order[n];

        auto c = job->c;
        auto p = job->p;

        // prepare next step in the job
        job->c.unset(v);
        job->p.unset(v);
        job_guard.unlock();

        // consider taking v
        c.set(v);

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c.popcount(), params, result, best_anywhere);
        }
        else {
            // get our coloured vertices
            ++result.nodes;

            if (child_job) {
                child_job->c = c;
                child_job->p = new_p;
                colourise<size_>(graph, child_job->p, child_job->p_order, child_job->colours);

                {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    steal_queue.push_front(child_job);
                    stealing_cv.notify_all();
                }

                while (true) {
                    std::unique_lock<std::mutex> child_job_guard(child_job->mutex);
                    if (! expand<size_>(graph, o, child_job, child_job_guard, nullptr,
                                stealing_mutex, stealing_cv, steal_queue, result, best_anywhere, params))
                        break;
                }

                {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    steal_queue.remove(child_job);
                }
            }
            else {
                std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
                colourise<size_>(graph, new_p, new_p_order, new_colours);
                expand_unstealable<size_>(graph, o, new_p_order, new_colours, c, new_p, result, best_anywhere, params);
            }
        }

        return true;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto dbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        MaxCliqueResult result;
        std::mutex result_mutex;
        result.size = params.initial_bound;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::vector<int> o(graph.size()); // vertex ordering

        // populate our order with every vertex initially
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::MinWidth:
                min_width_sort(graph, o, false);
                break;
            case MaxCliqueOrder::ExDegree:
                exdegree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::DynExDegree:
                dynexdegree_sort(graph, o, false);
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // initial ordering
        ++result.nodes;

        Job<size_> top_job;

        // first job
        top_job.c.resize(graph.size());
        top_job.p.resize(graph.size());
        top_job.p.set_all();
        colourise<size_>(bit_graph, top_job.p, top_job.p_order, top_job.colours);

        std::mutex stealing_mutex;
        std::condition_variable stealing_cv;
        bool finished = false;
        int number_busy = params.n_threads;
        std::list<Job<size_> *> steal_queue;

        std::list<std::thread> threads; // workers
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {

                auto start_time = std::chrono::steady_clock::now(); // local start time
                MaxCliqueResult tr; // local result

                Job<size_> this_thread_job;

                while (true) {
                    std::unique_lock<std::mutex> guard(top_job.mutex);
                    if (! expand<size_>(bit_graph, o,
                            &top_job, guard, &this_thread_job,
                            stealing_mutex, stealing_cv, steal_queue,
                            tr, best_anywhere, params))
                        break;
                }

                while (! finished) {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    if (0 == --number_busy) {
                        finished = true;
                        stealing_cv.notify_all();
                    } else {
                        bool found_something = false;
                        while (! steal_queue.empty()) {
                            std::unique_lock<std::mutex> steal_guard((*steal_queue.begin())->mutex);
                            if (! (*steal_queue.begin())->p.empty()) {
                                found_something = true;
                                ++number_busy;
                                guard.unlock();
                                expand<size_>(bit_graph, o, *steal_queue.begin(), steal_guard, nullptr,
                                        stealing_mutex, stealing_cv, steal_queue, tr, best_anywhere, params);
                                break;
                            }
                            else
                                steal_queue.pop_front();
                        }

                        if (! found_something)
                            stealing_cv.wait(guard);
                    }
                }

                auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                // merge results
                {
                    std::unique_lock<std::mutex> guard(result_mutex);
                    result.merge(tr);
                    result.times.push_back(overall_time);
                }
            }));
        }

        for (auto & t : threads)
            t.join();

        return result;
    }
}

template <MaxCliqueOrder order_>
auto parasols::dbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 1024, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return dbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return dbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return dbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return dbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return dbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return dbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return dbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return dbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return dbmcsa<order_, 256>(graph, params);
    else if (graph.size() < 512 * bits_per_word)
        return dbmcsa<order_, 512>(graph, params);
    else if (graph.size() < 1024 * bits_per_word)
        return dbmcsa<order_, 1024>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::MinWidth>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::ExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::DynExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<commit_msg>Threads are hard :(<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/dbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <graph/degree_sort.hh>
#include <graph/min_width_sort.hh>
#include <threads/atomic_incumbent.hh>

#include <algorithm>
#include <thread>
#include <condition_variable>
#include <mutex>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct Job
    {
        std::mutex mutex;
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
    };

    template <unsigned size_>
    auto found_possible_new_best(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,
            const FixedBitSet<size_> & c,
            int c_popcount,
            const MaxCliqueParams & params,
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size);
        }
    }

    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    template <unsigned size_>
    auto expand_unstealable(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const MaxCliqueParams & params
            ) -> void
    {
        auto c_popcount = c.popcount();
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
            return;

        auto v = p_order[n];

        // consider taking v
        c.set(v);
        ++c_popcount;

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere);
        }
        else {
            // get our coloured vertices
            ++result.nodes;
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            expand_unstealable<size_>(graph, o, new_p_order, new_colours, c, new_p, result, best_anywhere, params);
        }

        // now consider not taking v
        c.unset(v);
        p.unset(v);
        if (n > 0) {
            expand_unstealable<size_>(graph, o, p_order, colours, c, p, result, best_anywhere, params);
        }
    }

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,
            Job<size_> * job,
            std::unique_lock<std::mutex> & job_guard,
            Job<size_> * child_job,
            std::mutex & stealing_mutex,
            std::condition_variable & stealing_cv,
            std::list<Job<size_> *> & steal_queue,
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const MaxCliqueParams & params
            ) -> bool
    {
        auto job_c_popcount = job->c.popcount();
        int n = job->p.popcount() - 1;

        // bound, timeout or early exit?
        if (-1 == n || bound(job_c_popcount, job->colours[n], params, best_anywhere) || params.abort.load())
            return false;

        auto v = job->p_order[n];

        auto c = job->c;
        auto p = job->p;

        // prepare next step in the job
        job->c.unset(v);
        job->p.unset(v);
        job_guard.unlock();

        // consider taking v
        c.set(v);

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c.popcount(), params, result, best_anywhere);
        }
        else {
            // get our coloured vertices
            ++result.nodes;

            if (child_job) {
                child_job->c = c;
                child_job->p = new_p;
                colourise<size_>(graph, child_job->p, child_job->p_order, child_job->colours);

                {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    steal_queue.push_back(child_job);
                    stealing_cv.notify_all();
                }

                while (true) {
                    std::unique_lock<std::mutex> child_job_guard(child_job->mutex);
                    if (! expand<size_>(graph, o, child_job, child_job_guard, nullptr,
                                stealing_mutex, stealing_cv, steal_queue, result, best_anywhere, params))
                        break;
                }

                {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    steal_queue.remove(child_job);
                }
            }
            else {
                std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
                colourise<size_>(graph, new_p, new_p_order, new_colours);
                expand_unstealable<size_>(graph, o, new_p_order, new_colours, c, new_p, result, best_anywhere, params);
            }
        }

        return true;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto dbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        MaxCliqueResult result;
        std::mutex result_mutex;
        result.size = params.initial_bound;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::vector<int> o(graph.size()); // vertex ordering

        // populate our order with every vertex initially
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::MinWidth:
                min_width_sort(graph, o, false);
                break;
            case MaxCliqueOrder::ExDegree:
                exdegree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::DynExDegree:
                dynexdegree_sort(graph, o, false);
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // initial ordering
        ++result.nodes;

        Job<size_> top_job;

        // first job
        top_job.c.resize(graph.size());
        top_job.p.resize(graph.size());
        top_job.p.set_all();
        colourise<size_>(bit_graph, top_job.p, top_job.p_order, top_job.colours);

        std::mutex stealing_mutex;
        std::condition_variable stealing_cv;
        bool finished = false;
        int number_busy = params.n_threads;
        std::list<Job<size_> *> steal_queue;

        std::list<std::thread> threads; // workers
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {

                auto start_time = std::chrono::steady_clock::now(); // local start time
                MaxCliqueResult tr; // local result

                Job<size_> this_thread_job;

                while (true) {
                    std::unique_lock<std::mutex> guard(top_job.mutex);
                    if (! expand<size_>(bit_graph, o,
                            &top_job, guard, &this_thread_job,
                            stealing_mutex, stealing_cv, steal_queue,
                            tr, best_anywhere, params))
                        break;
                }

                while (true) {
                    std::unique_lock<std::mutex> guard(stealing_mutex);
                    if (finished)
                        break;

                    if (0 == --number_busy) {
                        finished = true;
                        stealing_cv.notify_all();
                    } else {
                        bool found_something = false;
                        for (auto s = steal_queue.begin() ; s != steal_queue.end() ; ++s) {
                            std::unique_lock<std::mutex> steal_guard((*s)->mutex);
                            if (! (*s)->p.empty()) {
                                found_something = true;
                                ++number_busy;
                                guard.unlock();
                                expand<size_>(bit_graph, o, *s, steal_guard, nullptr,
                                        stealing_mutex, stealing_cv, steal_queue, tr, best_anywhere, params);
                                break;
                            }
                        }

                        if (! found_something)
                            stealing_cv.wait(guard);
                    }
                }

                auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                // merge results
                {
                    std::unique_lock<std::mutex> guard(result_mutex);
                    result.merge(tr);
                    result.times.push_back(overall_time);
                }
            }));
        }

        for (auto & t : threads)
            t.join();

        return result;
    }
}

template <MaxCliqueOrder order_>
auto parasols::dbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 1024, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return dbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return dbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return dbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return dbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return dbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return dbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return dbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return dbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return dbmcsa<order_, 256>(graph, params);
    else if (graph.size() < 512 * bits_per_word)
        return dbmcsa<order_, 512>(graph, params);
    else if (graph.size() < 1024 * bits_per_word)
        return dbmcsa<order_, 1024>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::MinWidth>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::ExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::DynExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<|endoftext|>


<commit_before>#ifndef MJOLNIR_CORE_ENERGY_OBSERVER_HPP
#define MJOLNIR_CORE_ENERGY_OBSERVER_HPP
#include <mjolnir/util/is_finite.hpp>
#include <mjolnir/core/ObserverBase.hpp>
#include <mjolnir/core/Unit.hpp>
#include <iostream>
#include <fstream>
#include <iomanip>

namespace mjolnir
{

template<typename traitsT>
class EnergyObserver final : public ObserverBase<traitsT>
{
  public:
    using base_type         = ObserverBase<traitsT>;
    using traits_type       = typename base_type::traits_type;
    using real_type         = typename base_type::real_type;
    using coordinate_type   = typename base_type::coordinate_type;
    using system_type       = typename base_type::system_type;
    using forcefield_type   = typename base_type::forcefield_type;

  public:

    explicit EnergyObserver(const std::string& filename_prefix)
      : prefix_(filename_prefix), file_name_(filename_prefix + ".ene")
    {
        // clear files and throw an error if the files cannot be opened.
        this->clear_file(this->file_name_);
    }
    ~EnergyObserver() override {}

    void initialize(const std::size_t, const real_type,
                    const system_type& sys, const forcefield_type& ff) override
    {
        using phys_constants = physics::constants<real_type>;
        std::ofstream ofs(this->file_name_, std::ios::app);
        ofs << "# unit of length : " << phys_constants::length_unit()
            << ", unit of energy : " << phys_constants::energy_unit() << '\n';
        ofs << "# timestep  ";

        std::string names;
        ff->format_energy_name(names); // write into it
        ofs << names;

        ofs << " kinetic_energy";
        for(const auto& attr : sys.attributes())
        {
            ofs << " attribute:" << attr.first;
        }
        ofs << '\n';
        return;
    }

    // update column names and widths if forcefield changed.
    void update(const std::size_t,  const real_type,
                const system_type& sys, const forcefield_type& ff) override
    {
        std::ofstream ofs(this->file_name_, std::ios::app);
        ofs << "# timestep  ";

        std::string names;
        ff->format_energy_name(names); // write into it
        ofs << names;

        ofs << " kinetic_energy";
        for(const auto& attr : sys.attributes())
        {
            ofs << " attribute:" << attr.first;
        }
        ofs << '\n';
        return;
    }

    void output(const std::size_t step, const real_type,
                const system_type& sys, const forcefield_type& ff) override
    {
        bool is_ok = true;
        std::ofstream ofs(this->file_name_, std::ios::app);

        // if the width exceeds, operator<<(std::ostream, std::string) ignores
        // ostream::width and outputs whole string.
        ofs << std::setw(11) << std::left << std::to_string(step) << ' ';

        std::string energies;
        const auto potential_energy = ff->format_energy(sys, energies);
        ofs << energies;
        if(!is_finite(potential_energy))
        {
            MJOLNIR_GET_DEFAULT_LOGGER();
            MJOLNIR_LOG_ERROR("potential energy becomes NaN.");
            is_ok = false;
        }

        const auto Ek = this->calc_kinetic_energy(sys);
        ofs << std::setw(14) << std::right << std::fixed << Ek;
        if(!is_finite(Ek))
        {
            MJOLNIR_GET_DEFAULT_LOGGER();
            MJOLNIR_LOG_ERROR("kinetic energy becomes NaN.");
            is_ok = false;
        }

        for(const auto& attr : sys.attributes())
        {
            if(!is_finite(attr.second))
            {
                MJOLNIR_GET_DEFAULT_LOGGER();
                MJOLNIR_LOG_ERROR(attr.first, " becomes NaN.");
                is_ok = false;
            }
            ofs << ' ' << std::setw(10 + attr.first.size()) << std::right
                << std::fixed << attr.second;
        }
        ofs << std::endl; // flush before throwing an exception

        if(!is_ok)
        {
            throw std::runtime_error("Energy value becomes NaN");
        }
        return;
    }

    void finalize(const std::size_t, const real_type,
                  const system_type&, const forcefield_type&) override
    {/* do nothing. */}

    std::string const& prefix() const noexcept override {return this->prefix_;}

  private:

    void clear_file(const std::string& fname) const
    {
        std::ofstream ofs(fname);
        if(not ofs.good())
        {
            throw_exception<std::runtime_error>(
                "[error] mjolnir::EnergyObserver: file open error: ", fname);
        }
        return;
    }

    real_type calc_kinetic_energy(const system_type& sys) const
    {
        real_type k = 0.0;
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            k += math::length_sq(sys.velocity(i)) * sys.mass(i);
        }
        return k * 0.5;
    }

  private:

    std::string prefix_;
    std::string file_name_;
    std::vector<std::size_t> widths_; // column width to format energy values
};

#ifdef MJOLNIR_SEPARATE_BUILD
extern template class EnergyObserver<SimulatorTraits<double, UnlimitedBoundary>       >;
extern template class EnergyObserver<SimulatorTraits<float,  UnlimitedBoundary>       >;
extern template class EnergyObserver<SimulatorTraits<double, CuboidalPeriodicBoundary>>;
extern template class EnergyObserver<SimulatorTraits<float,  CuboidalPeriodicBoundary>>;
#endif

} // mjolnir
#endif//MJOLNIR_CORE_ENERGY_OBSERVER_HPP
<commit_msg>feat: output pressure if PBC is applied<commit_after>#ifndef MJOLNIR_CORE_ENERGY_OBSERVER_HPP
#define MJOLNIR_CORE_ENERGY_OBSERVER_HPP
#include <mjolnir/util/is_finite.hpp>
#include <mjolnir/core/ObserverBase.hpp>
#include <mjolnir/core/Unit.hpp>
#include <iostream>
#include <fstream>
#include <iomanip>

namespace mjolnir
{

template<typename traitsT>
class EnergyObserver final : public ObserverBase<traitsT>
{
  public:
    using base_type         = ObserverBase<traitsT>;
    using traits_type       = typename base_type::traits_type;
    using real_type         = typename base_type::real_type;
    using coordinate_type   = typename base_type::coordinate_type;
    using system_type       = typename base_type::system_type;
    using forcefield_type   = typename base_type::forcefield_type;
    using boundary_type     = typename traits_type::boundary_type;

  public:

    explicit EnergyObserver(const std::string& filename_prefix)
      : prefix_(filename_prefix), file_name_(filename_prefix + ".ene")
    {
        // clear files and throw an error if the files cannot be opened.
        this->clear_file(this->file_name_);
    }
    ~EnergyObserver() override {}

    void initialize(const std::size_t, const real_type,
                    const system_type& sys, const forcefield_type& ff) override
    {
        using phys_constants = physics::constants<real_type>;
        std::ofstream ofs(this->file_name_, std::ios::app);
        ofs << "# unit of length : " << phys_constants::length_unit()
            << ", unit of energy : " << phys_constants::energy_unit() << '\n';
        ofs << "# timestep  ";

        std::string names;
        ff->format_energy_name(names); // write into it
        ofs << names;

        ofs << " kinetic_energy";
        if(is_cuboidal_periodic_boundary<boundary_type>::value)
        {
            ofs << "             Pxx             Pyy             Pzz";
        }
        for(const auto& attr : sys.attributes())
        {
            ofs << " attribute:" << attr.first;
        }
        ofs << '\n';
        return;
    }

    // update column names and widths if forcefield changed.
    void update(const std::size_t,  const real_type,
                const system_type& sys, const forcefield_type& ff) override
    {
        std::ofstream ofs(this->file_name_, std::ios::app);
        ofs << "# timestep  ";

        std::string names;
        ff->format_energy_name(names); // write into it
        ofs << names;

        ofs << " kinetic_energy";
        if(is_cuboidal_periodic_boundary<boundary_type>::value)
        {
            ofs << "             Pxx             Pyy             Pzz";
        }
        for(const auto& attr : sys.attributes())
        {
            ofs << " attribute:" << attr.first;
        }
        ofs << '\n';
        return;
    }

    void output(const std::size_t step, const real_type,
                const system_type& sys, const forcefield_type& ff) override
    {
        bool is_ok = true;
        std::ofstream ofs(this->file_name_, std::ios::app);

        // if the width exceeds, operator<<(std::ostream, std::string) ignores
        // ostream::width and outputs whole string.
        ofs << std::setw(11) << std::left << std::to_string(step) << ' ';

        std::string energies;
        const auto potential_energy = ff->format_energy(sys, energies);
        ofs << energies;
        if(!is_finite(potential_energy))
        {
            MJOLNIR_GET_DEFAULT_LOGGER();
            MJOLNIR_LOG_ERROR("potential energy becomes NaN.");
            is_ok = false;
        }

        real_type Ek(0), Px(0), Py(0), Pz(0);
        std::tie(Ek, Px, Py, Pz) = this->calc_energy_and_pressure(sys,
                is_cuboidal_periodic_boundary<boundary_type>{});

        ofs << std::setw(14) << std::right << std::fixed << Ek;
        if(!is_finite(Ek))
        {
            MJOLNIR_GET_DEFAULT_LOGGER();
            MJOLNIR_LOG_ERROR("kinetic energy becomes NaN.");
            is_ok = false;
        }
        if(is_cuboidal_periodic_boundary<boundary_type>::value)
        {
            ofs << std::setw(16) << std::right << std::setprecision(8) << std::scientific << Px;
            ofs << std::setw(16) << std::right << std::setprecision(8) << std::scientific << Py;
            ofs << std::setw(16) << std::right << std::setprecision(8) << std::scientific << Pz;
        }

        for(const auto& attr : sys.attributes())
        {
            if(!is_finite(attr.second))
            {
                MJOLNIR_GET_DEFAULT_LOGGER();
                MJOLNIR_LOG_ERROR(attr.first, " becomes NaN.");
                is_ok = false;
            }
            ofs << ' ' << std::setw(10 + attr.first.size()) << std::right
                << std::fixed << attr.second;
        }
        ofs << std::endl; // flush before throwing an exception

        if(!is_ok)
        {
            throw std::runtime_error("Energy value becomes NaN");
        }
        return;
    }

    void finalize(const std::size_t, const real_type,
                  const system_type&, const forcefield_type&) override
    {/* do nothing. */}

    std::string const& prefix() const noexcept override {return this->prefix_;}

  private:

    std::tuple<real_type, real_type, real_type, real_type>
    calc_energy_and_pressure(const system_type& sys, std::true_type)
    {
        const auto cell_width = sys.boundary().width();
        const auto volume = math::X(cell_width) * math::Y(cell_width) * math::Z(cell_width);
        const auto rvolume = real_type(1) / volume;

        real_type Ek(0);
        real_type Px(0);
        real_type Py(0);
        real_type Pz(0);

        for(std::size_t i=0; i<sys.size(); ++i)
        {
            const auto  m = sys.mass(i);
            const auto& r = sys.position(i);
            const auto& v = sys.velocity(i);
            const auto& f = sys.force(i);

            Ek += math::length_sq(v) * m;
            Px += m * math::X(v) * math::X(v) + math::X(f) * math::X(r);
            Py += m * math::Y(v) * math::Y(v) + math::Y(f) * math::Y(r);
            Pz += m * math::Z(v) * math::Z(v) + math::Z(f) * math::Z(r);
        }
        Ek *= 0.5;
        Px *= rvolume;
        Py *= rvolume;
        Pz *= rvolume;

        return std::make_tuple(Ek, Px, Py, Pz);
    }

    std::tuple<real_type, real_type, real_type, real_type>
    calc_energy_and_pressure(const system_type& sys, std::false_type)
    {
        real_type Ek(0);
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            const auto  m = sys.mass(i);
            const auto& v = sys.velocity(i);
            Ek += math::length_sq(v) * m;
        }
        Ek *= 0.5;
        return std::make_tuple(Ek, real_type(0.0), real_type(0.0), real_type(0.0));
    }

    void clear_file(const std::string& fname) const
    {
        std::ofstream ofs(fname);
        if(not ofs.good())
        {
            throw_exception<std::runtime_error>(
                "[error] mjolnir::EnergyObserver: file open error: ", fname);
        }
        return;
    }

  private:

    std::string prefix_;
    std::string file_name_;
    std::vector<std::size_t> widths_; // column width to format energy values
};

#ifdef MJOLNIR_SEPARATE_BUILD
extern template class EnergyObserver<SimulatorTraits<double, UnlimitedBoundary>       >;
extern template class EnergyObserver<SimulatorTraits<float,  UnlimitedBoundary>       >;
extern template class EnergyObserver<SimulatorTraits<double, CuboidalPeriodicBoundary>>;
extern template class EnergyObserver<SimulatorTraits<float,  CuboidalPeriodicBoundary>>;
#endif

} // mjolnir
#endif//MJOLNIR_CORE_ENERGY_OBSERVER_HPP
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2014 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <sstream>

#include "cpp_utils/assert.hpp"
#include "cpp_utils/string.hpp"

#include "console.hpp"

std::string budget::format_code(int attr, int fg, int bg){
    std::stringstream stream;
    stream << "" << '\033' << "[" << attr << ";" << (fg + 30) << (bg + 40) << "m";
    return stream.str();
}

std::string budget::format_money(const budget::money& m){
    if(m.positive()){
        return "::green" + budget::to_string(m);
    } else if(m.negative()){
        return "::red" + budget::to_string(m);
    } else if(m.zero()){
        return budget::to_string(m);
    } else {
        return budget::to_string(m);
    }
}

std::string budget::format_money_reverse(const budget::money& m){
    if(m.positive()){
        return "::red" + budget::to_string(m);
    } else if(m.negative()){
        return "::green" + budget::to_string(m);
    } else if(m.zero()){
        return budget::to_string(m);
    } else {
        return budget::to_string(m);
    }
}

std::size_t budget::rsize(const std::string& value){
    auto v = value;

    if(v.substr(0, 5) == "::red"){
        v = v.substr(5);
    } else if(v.substr(0, 7) == "::green"){
        v = v.substr(7);
    }

    static wchar_t buf[1025];

    return mbstowcs(buf, v.c_str(), 1024);
}

bool budget::option(const std::string& option, std::vector<std::string>& args){
    auto before = args.size();
    args.erase(std::remove(args.begin(), args.end(), option), args.end());
    return before != args.size();
}

std::string budget::option_value(const std::string& option, std::vector<std::string>& args, const std::string& default_value){
    auto it = args.begin();
    auto end = args.end();

    auto value = default_value;

    while(it != end){
        if (it->find(option + "=") == 0){
            value = std::string(it->begin() + option.size() + 1, it->end());

            it = args.erase(it);
            end = args.end();
        } else {
            ++it;
        }
    }

    return value;
}

std::string budget::format(const std::string& v){
    if(v.substr(0, 5) == "::red"){
        auto value = v.substr(5);

        std::cout << "\033[0;31m";

        return value;
    } else if(v.substr(0, 7) == "::green"){
        auto value = v.substr(7);

        std::cout << "\033[0;32m";

        return value;
    }

    return v;
}

void budget::display_table(std::vector<std::string> columns, std::vector<std::vector<std::string>> contents, std::size_t groups){
    cpp_assert(groups > 0, "There must be at least 1 group");

    for(auto& row : contents){
        for(auto& cell : row){
            cpp::trim(cell);
        }
    }

    std::vector<std::size_t> widths;
    std::vector<std::size_t> header_widths;

    if(!contents.size()){
        for(auto& column : columns){
            widths.push_back(rsize(column));
        }
    } else {
        auto& first = contents[0];

        widths.assign(first.size(), 0);

        for(auto& row : contents){
            for(std::size_t i = 0; i < row.size(); ++i){
                widths[i] = std::max(widths[i], rsize(row[i]) + 1);
            }
        }
    }

    cpp_assert(widths.size() == groups * columns.size(), "Widths incorrectly computed");

    for(std::size_t i = 0; i < columns.size(); ++i){
        auto& column = columns[i];

        std::size_t width = 0;
        for(std::size_t j = i * groups; j < (i + 1) * groups; ++j){
            width += widths[j];
        }

        width = std::max(width, rsize(column));
        header_widths.push_back(width + (i < columns.size() - 1 && rsize(column) >= width ? 1 : 0));

        //The last space is not underlined
        --width;

        std::cout << format_code(4, 0, 7) << column << (width > rsize(column) ? std::string(width - rsize(column), ' ') : "") << format_code(0, 0, 7);

        //The very last column has no trailing space

        if(i < columns.size() - 1){
            std::cout << " ";
        }
    }

    std::cout << std::endl;

    for(std::size_t i = 0; i < contents.size(); ++i){
        auto& row = contents[i];

        std::cout << format_code(0, 0, 7);

        for(std::size_t j = 0; j < row.size(); j += groups){
            std::size_t acc_width = 0;

            //First columns of the group
            for(std::size_t k = 0; k < groups - 1; ++k){
                auto column = j + k;

                std::string value = format(row[column]);

                acc_width += widths[column];
                std::cout << value;

                //The content of the column can change the style, it is
                //important to reapply it

                std::cout << format_code(0, 0, 7);

                std::cout << std::string(widths[column] - rsize(value), ' ');
            }

            //The last column of the group

            auto last_column = j + (groups - 1);
            auto width = widths[last_column];
            acc_width += width;

            //Pad with spaces to fit the header column width

            if(header_widths[j / groups] > acc_width){
                width += header_widths[j / groups] - acc_width;
            } else if(last_column == row.size() - 1){
                --width;
            }

            auto value = format(row[last_column]);
            std::cout << value;

            //The content of the column can change the style, it is
            //important to reapply it

            std::cout << format_code(0, 0, 7);

            std::cout << std::string(width - rsize(row[last_column]), ' ');
        }

        std::cout << std::endl;
    }
}
<commit_msg>Clean<commit_after>//=======================================================================
// Copyright (c) 2013-2014 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <sstream>

#include "cpp_utils/assert.hpp"
#include "cpp_utils/string.hpp"

#include "console.hpp"

std::string budget::format_code(int attr, int fg, int bg){
    std::stringstream stream;
    stream << "" << '\033' << "[" << attr << ";" << (fg + 30) << (bg + 40) << "m";
    return stream.str();
}

std::string budget::format_money(const budget::money& m){
    if(m.positive()){
        return "::green" + budget::to_string(m);
    } else if(m.negative()){
        return "::red" + budget::to_string(m);
    } else {
        return budget::to_string(m);
    }
}

std::string budget::format_money_reverse(const budget::money& m){
    if(m.positive()){
        return "::red" + budget::to_string(m);
    } else if(m.negative()){
        return "::green" + budget::to_string(m);
    } else if(m.zero()){
        return budget::to_string(m);
    }
}

std::size_t budget::rsize(const std::string& value){
    auto v = value;

    if(v.substr(0, 5) == "::red"){
        v = v.substr(5);
    } else if(v.substr(0, 7) == "::green"){
        v = v.substr(7);
    }

    static wchar_t buf[1025];

    return mbstowcs(buf, v.c_str(), 1024);
}

bool budget::option(const std::string& option, std::vector<std::string>& args){
    auto before = args.size();
    args.erase(std::remove(args.begin(), args.end(), option), args.end());
    return before != args.size();
}

std::string budget::option_value(const std::string& option, std::vector<std::string>& args, const std::string& default_value){
    auto it = args.begin();
    auto end = args.end();

    auto value = default_value;

    while(it != end){
        if (it->find(option + "=") == 0){
            value = std::string(it->begin() + option.size() + 1, it->end());

            it = args.erase(it);
            end = args.end();
        } else {
            ++it;
        }
    }

    return value;
}

std::string budget::format(const std::string& v){
    if(v.substr(0, 5) == "::red"){
        auto value = v.substr(5);

        std::cout << "\033[0;31m";

        return value;
    } else if(v.substr(0, 7) == "::green"){
        auto value = v.substr(7);

        std::cout << "\033[0;32m";

        return value;
    }

    return v;
}

void budget::display_table(std::vector<std::string> columns, std::vector<std::vector<std::string>> contents, std::size_t groups){
    cpp_assert(groups > 0, "There must be at least 1 group");

    for(auto& row : contents){
        for(auto& cell : row){
            cpp::trim(cell);
        }
    }

    std::vector<std::size_t> widths;
    std::vector<std::size_t> header_widths;

    if(!contents.size()){
        for(auto& column : columns){
            widths.push_back(rsize(column));
        }
    } else {
        auto& first = contents[0];

        widths.assign(first.size(), 0);

        for(auto& row : contents){
            for(std::size_t i = 0; i < row.size(); ++i){
                widths[i] = std::max(widths[i], rsize(row[i]) + 1);
            }
        }
    }

    cpp_assert(widths.size() == groups * columns.size(), "Widths incorrectly computed");

    for(std::size_t i = 0; i < columns.size(); ++i){
        auto& column = columns[i];

        std::size_t width = 0;
        for(std::size_t j = i * groups; j < (i + 1) * groups; ++j){
            width += widths[j];
        }

        width = std::max(width, rsize(column));
        header_widths.push_back(width + (i < columns.size() - 1 && rsize(column) >= width ? 1 : 0));

        //The last space is not underlined
        --width;

        std::cout << format_code(4, 0, 7) << column << (width > rsize(column) ? std::string(width - rsize(column), ' ') : "") << format_code(0, 0, 7);

        //The very last column has no trailing space

        if(i < columns.size() - 1){
            std::cout << " ";
        }
    }

    std::cout << std::endl;

    for(std::size_t i = 0; i < contents.size(); ++i){
        auto& row = contents[i];

        std::cout << format_code(0, 0, 7);

        for(std::size_t j = 0; j < row.size(); j += groups){
            std::size_t acc_width = 0;

            //First columns of the group
            for(std::size_t k = 0; k < groups - 1; ++k){
                auto column = j + k;

                std::string value = format(row[column]);

                acc_width += widths[column];
                std::cout << value;

                //The content of the column can change the style, it is
                //important to reapply it

                std::cout << format_code(0, 0, 7);

                std::cout << std::string(widths[column] - rsize(value), ' ');
            }

            //The last column of the group

            auto last_column = j + (groups - 1);
            auto width = widths[last_column];
            acc_width += width;

            //Pad with spaces to fit the header column width

            if(header_widths[j / groups] > acc_width){
                width += header_widths[j / groups] - acc_width;
            } else if(last_column == row.size() - 1){
                --width;
            }

            auto value = format(row[last_column]);
            std::cout << value;

            //The content of the column can change the style, it is
            //important to reapply it

            std::cout << format_code(0, 0, 7);

            std::cout << std::string(width - rsize(row[last_column]), ' ');
        }

        std::cout << std::endl;
    }
}
<|endoftext|>