<commit_before>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  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.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "TestingEnvironment.hpp"
#include "ShaderMacroHelper.hpp"

#include "gtest/gtest.h"

using namespace Diligent;
using namespace Diligent::Testing;

namespace Diligent
{

TEST(WaveOpTest, CompileShader_HLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();

    if (!pDevice->GetDeviceInfo().Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }
    if (!pEnv->HasDXCompiler())
    {
        GTEST_SKIP() << "HLSL source code with wave operations can be compiled only by DXC";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetAdapterInfo().WaveOp;

    auto WaveOpFeatures = WaveOpProps.Features;

    Uint32 DxcMajorVer = 0;
    Uint32 DxcMinorVer = 0;
    pEnv->GetDXCompilerVersion(DxcMajorVer, DxcMinorVer);
    if (!(DxcMajorVer >= 2 || (DxcMajorVer == 1 && DxcMinorVer >= 5)))
    {
        // There is a bug in older versions of DXC that causes the following error:
        //      opcode 'QuadReadAcross' should only be used in 'Pixel Shader'
        WaveOpFeatures &= ~WAVE_FEATURE_QUAD;
    }

    ASSERT_NE(WaveOpFeatures, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpFeatures & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    ShaderMacroHelper Macros;
    Macros.AddShaderMacro("SUBGROUP_SIZE", WaveOpProps.MinSize);

    // clang-format off
    Macros.AddShaderMacro("WAVE_FEATURE_BASIC",      (WaveOpFeatures & WAVE_FEATURE_BASIC)      != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_VOTE",       (WaveOpFeatures & WAVE_FEATURE_VOTE)       != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_ARITHMETIC", (WaveOpFeatures & WAVE_FEATURE_ARITHMETIC) != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_BALLOUT",    (WaveOpFeatures & WAVE_FEATURE_BALLOUT)    != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_QUAD",       (WaveOpFeatures & WAVE_FEATURE_QUAD)       != 0);
    // clang-format on

    static const char Source[] = R"(
RWByteAddressBuffer g_RWBuffer;

[numthreads(SUBGROUP_SIZE, 1, 1)]
void main(uint DTid : SV_DispatchThreadID)
{
    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = WaveGetLaneCount();
        uint  lineIndex = WaveGetLaneIndex();
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (WaveActiveAllTrue(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = WaveActiveSum(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = WaveActiveCountBits((DTid & 2) == 0);
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = QuadReadAcrossDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_RWBuffer.Store(DTid, Accum);
}
)";

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_HLSL;
    ShaderCI.ShaderCompiler  = SHADER_COMPILER_DXC;
    ShaderCI.HLSLVersion     = {6, 0};
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source;
    ShaderCI.Macros          = Macros;

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

TEST(WaveOpTest, CompileShader_GLSL)
{
    auto* const pEnv       = TestingEnvironment::GetInstance();
    auto* const pDevice    = pEnv->GetDevice();
    const auto& DeviceInfo = pDevice->GetDeviceInfo();

    if (!DeviceInfo.IsVulkanDevice() && !DeviceInfo.IsGLDevice())
    {
        GTEST_SKIP();
    }
    if (!DeviceInfo.Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetAdapterInfo().WaveOp;

    ASSERT_NE(WaveOpProps.Features, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.Features & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    std::stringstream ShaderSourceStream;
    ShaderSourceStream << "#version 450\n\n";
    ShaderSourceStream << "#define SUBGROUP_SIZE " << WaveOpProps.MinSize << "\n";

    // clang-format off
    ShaderSourceStream << "#define WAVE_FEATURE_BASIC "            << int{(WaveOpProps.Features & WAVE_FEATURE_BASIC)            != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_VOTE "             << int{(WaveOpProps.Features & WAVE_FEATURE_VOTE)             != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_ARITHMETIC "       << int{(WaveOpProps.Features & WAVE_FEATURE_ARITHMETIC)       != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_BALLOUT "          << int{(WaveOpProps.Features & WAVE_FEATURE_BALLOUT)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE "          << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE_RELATIVE " << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE_RELATIVE) != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_CLUSTERED "        << int{(WaveOpProps.Features & WAVE_FEATURE_CLUSTERED)        != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_QUAD "             << int{(WaveOpProps.Features & WAVE_FEATURE_QUAD)             != 0} << "\n";
    // clang-format on

    static const char ShaderBody[] = R"(
#if WAVE_FEATURE_BASIC
#    extension GL_KHR_shader_subgroup_basic: enable
#endif
#if WAVE_FEATURE_VOTE
#    extension GL_KHR_shader_subgroup_vote: enable
#endif
#if WAVE_FEATURE_BALLOUT
#    extension GL_KHR_shader_subgroup_ballot: enable
#endif
#if WAVE_FEATURE_ARITHMETIC
#    extension GL_KHR_shader_subgroup_arithmetic: enable
#endif
#if WAVE_FEATURE_SHUFFLE
#    extension GL_KHR_shader_subgroup_shuffle: enable
#endif
#if WAVE_FEATURE_SHUFFLE_RELATIVE
#    extension GL_KHR_shader_subgroup_shuffle_relative: enable
#endif
#if WAVE_FEATURE_CLUSTERED
#    extension GL_KHR_shader_subgroup_clustered: enable
#endif
#if WAVE_FEATURE_QUAD
#    extension GL_KHR_shader_subgroup_quad: enable
#endif

layout(local_size_x = SUBGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

layout(std140) writeonly buffer WBuffer
{
    uint g_WBuffer[];
};

void main()
{
    const uint DTid = gl_LocalInvocationID.x;

    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = gl_SubgroupSize;
        uint  lineIndex = gl_SubgroupInvocationID;
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (subgroupAll(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = subgroupAdd(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = subgroupBallotExclusiveBitCount(subgroupBallot((DTid & 1) == 0));
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE
    {
        vec4 temp      = vec4(float(DTid));
        vec4 blendWith = subgroupShuffle(temp, (DTid + 5) & 7);
        Accum += (dot(temp, temp) < 0.0 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE_RELATIVE
    {
        vec4 temp = vec4(float(DTid));
        for (uint i = 2; i < gl_SubgroupSize; i *= 2)
        {
            vec4 other = subgroupShuffleUp(temp, i);

            if (i <= gl_SubgroupInvocationID)
                temp = temp * other;
        }
        Accum += (dot(temp, temp) > 0.5 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_CLUSTERED
    {
        uint maxId = subgroupClusteredMax(DTid, 4*4);
        Accum += (maxId+1 == SUBGROUP_SIZE ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = subgroupQuadSwapDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_WBuffer[DTid] = Accum;
}
)";
    ShaderSourceStream << ShaderBody;
    const String Source = ShaderSourceStream.str();

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM;
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source.c_str();

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

} // namespace Diligent
<commit_msg>Metal: added WaveOp test<commit_after>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  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.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "TestingEnvironment.hpp"
#include "ShaderMacroHelper.hpp"

#include "gtest/gtest.h"

using namespace Diligent;
using namespace Diligent::Testing;

namespace Diligent
{

TEST(WaveOpTest, CompileShader_HLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();

    if (!pDevice->GetDeviceInfo().Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }
    if (!pEnv->HasDXCompiler())
    {
        GTEST_SKIP() << "HLSL source code with wave operations can be compiled only by DXC";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetAdapterInfo().WaveOp;

    auto WaveOpFeatures = WaveOpProps.Features;

    Uint32 DxcMajorVer = 0;
    Uint32 DxcMinorVer = 0;
    pEnv->GetDXCompilerVersion(DxcMajorVer, DxcMinorVer);
    if (!(DxcMajorVer >= 2 || (DxcMajorVer == 1 && DxcMinorVer >= 5)))
    {
        // There is a bug in older versions of DXC that causes the following error:
        //      opcode 'QuadReadAcross' should only be used in 'Pixel Shader'
        WaveOpFeatures &= ~WAVE_FEATURE_QUAD;
    }

    ASSERT_NE(WaveOpFeatures, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpFeatures & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    ShaderMacroHelper Macros;
    Macros.AddShaderMacro("SUBGROUP_SIZE", WaveOpProps.MinSize);

    // clang-format off
    Macros.AddShaderMacro("WAVE_FEATURE_BASIC",      (WaveOpFeatures & WAVE_FEATURE_BASIC)      != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_VOTE",       (WaveOpFeatures & WAVE_FEATURE_VOTE)       != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_ARITHMETIC", (WaveOpFeatures & WAVE_FEATURE_ARITHMETIC) != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_BALLOUT",    (WaveOpFeatures & WAVE_FEATURE_BALLOUT)    != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_QUAD",       (WaveOpFeatures & WAVE_FEATURE_QUAD)       != 0);
    // clang-format on

    static const char Source[] = R"(
RWByteAddressBuffer g_RWBuffer;

[numthreads(SUBGROUP_SIZE, 1, 1)]
void main(uint DTid : SV_DispatchThreadID)
{
    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = WaveGetLaneCount();
        uint  lineIndex = WaveGetLaneIndex();
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (WaveActiveAllTrue(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = WaveActiveSum(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = WaveActiveCountBits((DTid & 2) == 0);
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = QuadReadAcrossDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_RWBuffer.Store(DTid, Accum);
}
)";

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_HLSL;
    ShaderCI.ShaderCompiler  = SHADER_COMPILER_DXC;
    ShaderCI.HLSLVersion     = {6, 0};
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source;
    ShaderCI.Macros          = Macros;

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

TEST(WaveOpTest, CompileShader_GLSL)
{
    auto* const pEnv       = TestingEnvironment::GetInstance();
    auto* const pDevice    = pEnv->GetDevice();
    const auto& DeviceInfo = pDevice->GetDeviceInfo();

    if (!DeviceInfo.IsVulkanDevice() && !DeviceInfo.IsGLDevice())
    {
        GTEST_SKIP();
    }
    if (!DeviceInfo.Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetAdapterInfo().WaveOp;

    ASSERT_NE(WaveOpProps.Features, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.Features & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    std::stringstream ShaderSourceStream;
    ShaderSourceStream << "#version 450\n\n";
    ShaderSourceStream << "#define SUBGROUP_SIZE " << WaveOpProps.MinSize << "\n";

    // clang-format off
    ShaderSourceStream << "#define WAVE_FEATURE_BASIC "            << int{(WaveOpProps.Features & WAVE_FEATURE_BASIC)            != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_VOTE "             << int{(WaveOpProps.Features & WAVE_FEATURE_VOTE)             != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_ARITHMETIC "       << int{(WaveOpProps.Features & WAVE_FEATURE_ARITHMETIC)       != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_BALLOUT "          << int{(WaveOpProps.Features & WAVE_FEATURE_BALLOUT)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE "          << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE_RELATIVE " << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE_RELATIVE) != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_CLUSTERED "        << int{(WaveOpProps.Features & WAVE_FEATURE_CLUSTERED)        != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_QUAD "             << int{(WaveOpProps.Features & WAVE_FEATURE_QUAD)             != 0} << "\n";
    // clang-format on

    static const char ShaderBody[] = R"(
#if WAVE_FEATURE_BASIC
#    extension GL_KHR_shader_subgroup_basic: enable
#endif
#if WAVE_FEATURE_VOTE
#    extension GL_KHR_shader_subgroup_vote: enable
#endif
#if WAVE_FEATURE_BALLOUT
#    extension GL_KHR_shader_subgroup_ballot: enable
#endif
#if WAVE_FEATURE_ARITHMETIC
#    extension GL_KHR_shader_subgroup_arithmetic: enable
#endif
#if WAVE_FEATURE_SHUFFLE
#    extension GL_KHR_shader_subgroup_shuffle: enable
#endif
#if WAVE_FEATURE_SHUFFLE_RELATIVE
#    extension GL_KHR_shader_subgroup_shuffle_relative: enable
#endif
#if WAVE_FEATURE_CLUSTERED
#    extension GL_KHR_shader_subgroup_clustered: enable
#endif
#if WAVE_FEATURE_QUAD
#    extension GL_KHR_shader_subgroup_quad: enable
#endif

layout(local_size_x = SUBGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

layout(std140) writeonly buffer WBuffer
{
    uint g_WBuffer[];
};

void main()
{
    const uint DTid = gl_LocalInvocationID.x;

    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = gl_SubgroupSize;
        uint  lineIndex = gl_SubgroupInvocationID;
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (subgroupAll(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = subgroupAdd(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = subgroupBallotExclusiveBitCount(subgroupBallot((DTid & 1) == 0));
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE
    {
        vec4 temp      = vec4(float(DTid));
        vec4 blendWith = subgroupShuffle(temp, (DTid + 5) & 7);
        Accum += (dot(blendWith, blendWith) < 0.0 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE_RELATIVE
    {
        vec4 temp = vec4(float(DTid));
        for (uint i = 2; i < gl_SubgroupSize; i *= 2)
        {
            vec4 other = subgroupShuffleUp(temp, i);

            if (i <= gl_SubgroupInvocationID)
                temp = temp * other;
        }
        Accum += (dot(temp, temp) > 0.5 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_CLUSTERED
    {
        uint maxId = subgroupClusteredMax(DTid, 4*4);
        Accum += (maxId+1 == SUBGROUP_SIZE ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = subgroupQuadSwapDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_WBuffer[DTid] = Accum;
}
)";
    ShaderSourceStream << ShaderBody;
    const String Source = ShaderSourceStream.str();

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM;
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source.c_str();

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

TEST(WaveOpTest, CompileShader_MSL)
{
    auto* const pEnv       = TestingEnvironment::GetInstance();
    auto* const pDevice    = pEnv->GetDevice();
    const auto& DeviceInfo = pDevice->GetDeviceInfo();

    if (!DeviceInfo.IsMetalDevice())
    {
        GTEST_SKIP();
    }
    if (!DeviceInfo.Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetAdapterInfo().WaveOp;

    ASSERT_NE(WaveOpProps.Features, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.Features & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    std::stringstream ShaderSourceStream;
    // clang-format off
    ShaderSourceStream << "#define WAVE_FEATURE_BASIC "            << int{(WaveOpProps.Features & WAVE_FEATURE_BASIC)            != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_VOTE "             << int{(WaveOpProps.Features & WAVE_FEATURE_VOTE)             != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_ARITHMETIC "       << int{(WaveOpProps.Features & WAVE_FEATURE_ARITHMETIC)       != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_BALLOUT "          << int{(WaveOpProps.Features & WAVE_FEATURE_BALLOUT)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE "          << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE_RELATIVE " << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE_RELATIVE) != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_CLUSTERED "        << int{(WaveOpProps.Features & WAVE_FEATURE_CLUSTERED)        != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_QUAD "             << int{(WaveOpProps.Features & WAVE_FEATURE_QUAD)             != 0} << "\n";
    // clang-format on

    static const char ShaderBody[] = R"(
#include <metal_stdlib>
#include <simd/simd.h>
#include <metal_simdgroup>
using namespace metal;

kernel void CSMain(
#if WAVE_FEATURE_BASIC
    uint LaneIndex  [[thread_index_in_simdgroup]],
    uint WaveSize   [[threads_per_simdgroup]],
#endif
#if WAVE_FEATURE_QUAD
    uint QuadId     [[thread_index_in_quadgroup]],
#endif
    device uint* g_WBuffer [[buffer(0)]],
    uint         DTid      [[thread_index_in_threadgroup]]
)
{
    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        Accum += (LaneIndex % WaveSize);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (simd_all(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = simd_sum(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        float val = simd_broadcast(float(DTid) * 0.1f, ushort(LaneIndex));
        Accum += (val > 3.5f);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE
    {
        float4 temp      = float4(float(DTid));
        float4 blendWith = simd_shuffle(temp, ushort((DTid + 5) & 7));
        Accum += (dot(blendWith, blendWith) < 0.0 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE_RELATIVE
    {
        float4 temp = float4(float(DTid));
        for (uint i = 2; i < WaveSize; i *= 2)
        {
            float4 other = simd_shuffle_up(temp, ushort(i));

            if (i <= LaneIndex)
                temp = temp * other;
        }
        Accum += (dot(temp, temp) > 0.5 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        float val = quad_broadcast(float(DTid) * 0.1f, ushort(LaneIndex));
        Accum += (val > 2.5f);
    }
    #endif

    g_WBuffer[DTid] = Accum;
}
)";

    ShaderSourceStream << ShaderBody;
    const String Source = ShaderSourceStream.str();

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_MSL;
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "CSMain";
    ShaderCI.Source          = Source.c_str();

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

} // namespace Diligent
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2102
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1261291 by johtaylo@johtaylo-JTBUILDER03-increment on 2016/04/23 03:00:11<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2103
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2851
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1754722 by chui@ocl-promo-incrementor on 2019/03/12 03:00:29<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2852
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1587
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1055121 by johtaylo@johtaylo-JTBUILDER03-increment on 2014/07/15 03:00:12<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1588
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1763
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1134141 by johtaylo@johtaylo-JTBUILDER03-increment on 2015/03/25 03:00:11<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1764
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2098
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1259389 by johtaylo@johtaylo-JTBUILDER03-increment on 2016/04/19 03:00:11<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2099
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3039
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 2031718 by chui@ocl-promo-incrementor on 2019/11/16 03:00:09<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3040
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   gdcm
  Module:    gdcmPixelWriteConvert.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
  l'Image). All rights reserved. See Doc/License.txt or
  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(__BORLANDC__)
#pragma warn -8057 /* parameter is never used */
#endif

#include "gdcmDebug.h"
#include "gdcmPixelWriteConvert.h"
#include "gdcmFile.h"
#include "gdcmUtil.h"

#include <vector>
#include <stdlib.h>

#define WITHOFFSETTABLE 1

namespace gdcm
{
//-----------------------------------------------------------------------------
// Constructor / Destructor
/**
 * \brief Constructor
 */
PixelWriteConvert::PixelWriteConvert()
{
   ReadData     = 0;
   ReadDataSize = 0;

   UserData     = 0;
   UserDataSize = 0;
   Compressed   = false;
}

/**
 * \brief Destructor
 */
PixelWriteConvert::~PixelWriteConvert()
{
   if( Compressed )
     {
     delete[] UserData;
     }
}

//-----------------------------------------------------------------------------
// Public
/**
 * \brief   sets Read Data (and size)
 * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
 *                     just cast the pointer for calling the method)
 * @param   size data size, in bytes
 */
void PixelWriteConvert::SetReadData(uint8_t *data, size_t size)
{
   ReadData = data;
   ReadDataSize = size;
}

/**
 * \brief   Sets the internal pointer to the caller's inData
 *          image representation, BUT WITHOUT COPYING THE DATA.
 *          - 'image' Pixels are presented as C-like 2D arrays : line per line.
 *          - 'volume'Pixels are presented as C-like 3D arrays : plane per plane
 * \warning Since the pixels are not copied, it is the caller's responsability
 *          not to deallocate its data before gdcm uses them (e.g. with
 *          the Write() method )
 * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
 *                     just cast the pointer for calling the method)
 * @param   size size, in bytes.
 */
void PixelWriteConvert::SetUserData(uint8_t *data, size_t size)
{
   UserData = data;
   UserDataSize = size;
}

/**
 * \brief   Get Data (UserData or ReadData)
 * @return  data (uint8_t is for prototyping. if your data is
 *                    *not* uint8_t, just cast the returned pointer)
 */
uint8_t *PixelWriteConvert::GetData()
{
   if ( UserData )
   {
      return UserData;
   }
   else
   {
      return ReadData;
   }
}

/**
 * \brief   Get Data Size (UserData or ReadData)
 * @return  size, in bytes.
 */
size_t PixelWriteConvert::GetDataSize()
{
   if ( UserData )
   {
      return UserDataSize;
   }
   else
   {
      return ReadDataSize;
   }
}

typedef std::pair<size_t, uint32_t> JpegPair; //offset, jpeg size
typedef std::vector<JpegPair> JpegVector;

bool gdcm_write_JPEG2000_file (std::ostream *of, char *inputdata, size_t inputlength,
  int image_width, int image_height, int numZ, int sample_pixel, int bitsallocated,
  int sign, int quality, size_t &);

void WriteDICOMItems(std::ostream *fp, JpegVector &v)
{
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t dummy = 0x12345678;
  size_t offset = fp->tellp();
  JpegPair jp;
  jp.first = offset;
  v.push_back(jp);
  gdcm::binary_write(*fp, dummy);
}

// PS 3.5, page 66
#ifdef NDEBUG
void EncodeWithoutBasicOffsetTable(std::ostream *fp, int)// JpegVector& v) //, uint32_t length)
{
#else
void EncodeWithoutBasicOffsetTable(std::ostream *fp, int numFrag)// JpegVector& v) //, uint32_t length)
{
  assert( numFrag == 1);
#endif
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t item_length = 0x0000;
  gdcm::binary_write(*fp, item_length);

}

// PS 3.5, page 67
void EncodeWithBasicOffsetTable(std::ostream *fp, int numFrag, size_t &start)
{
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t item_length = numFrag*4; // sizeof(uint32_t)
  gdcm::binary_write(*fp, item_length);

  // Just prepare the space
  start = fp->tellp(); //to be able to rewind
  for(int i=0; i<numFrag;++i)
    {
    uint32_t dummy = 0x0000;
    gdcm::binary_write(*fp, dummy);
    }
}

void UpdateBasicOffsetTable(std::ostream *fp, JpegVector const &v, size_t pos)
{
  JpegVector::const_iterator i;
  fp->seekp( pos );
  const JpegPair &first = v[0];
  for(i=v.begin(); i!=v.end(); ++i)
    {
    const JpegPair &jp = *i;
    if(i == v.begin() ){ assert( jp.first - first.first == 0); }
    uint32_t offset = (uint32_t)(jp.first - first.first);
    gdcm::binary_write(*fp, offset);
    //std::cerr << "Updating Table:" << jp.first - first.first << std::endl;
    }
}

void UpdateJpegFragmentSize(std::ostream *fp, JpegVector const &v)
{
  JpegVector::const_iterator i;
  for(i= v.begin(); i!=v.end(); ++i)
    {
    const JpegPair &jp = *i;
    fp->seekp( jp.first );
    uint32_t length = jp.second;
    gdcm::binary_write(*fp, length);
    //std::cerr << "Updating:" << jp.first << "," << jp.second << std::endl;
    }
}

void CloseJpeg(std::ostream *fp, JpegVector &v)
{
  // sequence terminator
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe0dd;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);

  uint32_t length = 0x0;
  gdcm::binary_write(*fp, length);

  // Jpeg is done, now update the frag length
  UpdateJpegFragmentSize(fp, v);
}

// I need to pass the File*. I do not understand how PixelWriteConvert is supposed
// to access this information otherwise
// size can now be computer from File attributes (what an API...)
void PixelWriteConvert::SetCompressJPEG2000UserData(uint8_t *data, size_t size, File *image)
{
  Compressed = true;
  //char * userData = reinterpret_cast<char*>(UserData);

   itksys_ios::stringstream *of = new itksys_ios::stringstream(); // ostringstream: debug / sunos
    int xsize = image->GetXSize();
   int ysize = image->GetYSize();
  int zsize =  image->GetZSize();
    int samplesPerPixel = image->GetSamplesPerPixel();
   //std::cout << "X: " << xsize << std::endl;
   //std::cout << "Y: " << ysize << std::endl;
   //std::cout << "Sample: " << samplesPerPixel << std::endl;
    int bitsallocated = image->GetBitsAllocated();
    int sign = image->IsSignedPixelData();
   unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
    assert( fragment_size*zsize == size );

   JpegVector JpegFragmentSize;
#if WITHOFFSETTABLE
   size_t bots; //basic offset table start
   EncodeWithBasicOffsetTable(of, zsize, bots);
#else
   EncodeWithoutBasicOffsetTable(of, 1);
#endif
   uint8_t *pImageData = data;
   for(int i=0; i<zsize;i++)
     {
     WriteDICOMItems(of, JpegFragmentSize);
     size_t beg = of->tellp();
     size_t nbbytes = 0;
     gdcm_write_JPEG2000_file(of, (char*)pImageData,size,
       image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
       image->GetBitsAllocated(), sign, 100, nbbytes);
     //userData, UserDataSize);
     //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
     //       samplesPerPixel, quality, JpegFragmentSize);
     //assert( !(fragment_size % 2) );
     // Update the JpegVector with offset
     size_t end = of->tellp();
     if( (end - beg) != nbbytes )
       {
       gdcmErrorMacro( "Go fix your compiler implementation of stringstream." );
       UserData = 0;
       UserDataSize = 0;
       return;
       }
     //static int i = 0;
     JpegPair &jp = JpegFragmentSize[i];
     jp.second = (uint32_t)(end-beg);
     if( ((end-beg) % 2) )
       {
       of->put( '\0' );
       jp.second += 1;
       }
     assert( !(jp.second % 2) );
     //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
     pImageData += fragment_size;
     }
   CloseJpeg(of, JpegFragmentSize);
#if WITHOFFSETTABLE
   UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
#endif

   size_t of_size = of->str().size();
   gdcmErrorMacro( "Of_size: " << of_size ); // FIXME: remote debug of sunos
   UserData = new uint8_t[of_size];
   memcpy(UserData, of->str().c_str(), of_size);
   UserDataSize = of_size;
   delete of;
}

bool gdcm_write_JPEG_file8 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);
bool gdcm_write_JPEG_file12 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);
bool gdcm_write_JPEG_file16 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);

void PixelWriteConvert::SetCompressJPEGUserData(uint8_t *data, size_t size, File *image)
{
  (void)data;
  (void)size;
  (void)image;
  Compressed = true;
  //char * userData = reinterpret_cast<char*>(UserData);

   itksys_ios::ostringstream *of = new itksys_ios::ostringstream();
    int xsize = image->GetXSize();
   int ysize = image->GetYSize();
  int zsize =  image->GetZSize();
    int samplesPerPixel = image->GetSamplesPerPixel();
   //std::cout << "X: " << xsize << std::endl;
   //std::cout << "Y: " << ysize << std::endl;
   //std::cout << "Sample: " << samplesPerPixel << std::endl;
    int bitsallocated = image->GetBitsAllocated();
   unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
    assert( fragment_size*zsize == size );

   JpegVector JpegFragmentSize;
#if WITHOFFSETTABLE
   size_t bots; //basic offset table start
   EncodeWithBasicOffsetTable(of, zsize, bots);
#else
   EncodeWithoutBasicOffsetTable(of, 1);
#endif
   uint8_t *pImageData = data;
   for(int i=0; i<zsize;i++)
     {
     WriteDICOMItems(of, JpegFragmentSize);
  size_t beg = of->tellp();
     if( bitsallocated == 8 )
       {
       gdcm_write_JPEG_file8(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100 );
       }
     else if (bitsallocated <= 12)
       {
       assert( bitsallocated >= 8 );
       gdcm_write_JPEG_file12(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100);
       }
     else if (bitsallocated <= 16)
       {
       assert( bitsallocated >= 12 );
       gdcm_write_JPEG_file16(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100);
       }
     else
       {
       abort();
       }
    size_t end = of->tellp();
    //static int i = 0;
    JpegPair &jp = JpegFragmentSize[i];
      jp.second = (uint32_t)(end-beg);
    if( ((end-beg) % 2) )
      {
      of->put( '\0' );
      jp.second += 1;
      }
    assert( !(jp.second % 2) );
    //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
    //++i;

  //JpegPair &jp = v[0];
  //jp.second = 15328;

     //userData, UserDataSize);
     //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
     //       samplesPerPixel, quality, JpegFragmentSize);
     //assert( !(fragment_size % 2) );
     pImageData += fragment_size;
     }
   CloseJpeg(of, JpegFragmentSize);
#if WITHOFFSETTABLE
   UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
#endif

   size_t of_size = of->str().size();
   UserData = new uint8_t[of_size];
   memcpy(UserData, of->str().c_str(), of_size);
   UserDataSize = of_size;
   delete of;
}

//-----------------------------------------------------------------------------
// Protected
//bool PixelWriteConvert::CompressJPEG2000(uint8_t *data, size_t size)
//{
//}

//-----------------------------------------------------------------------------
// Private

//-----------------------------------------------------------------------------
// Print

//-----------------------------------------------------------------------------
} // end namespace gdcm
<commit_msg>ENH: temp fix on sunos. Skip the offset table and see if test pass<commit_after>/*=========================================================================

  Program:   gdcm
  Module:    gdcmPixelWriteConvert.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
  l'Image). All rights reserved. See Doc/License.txt or
  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(__BORLANDC__)
#pragma warn -8057 /* parameter is never used */
#endif

#include "gdcmDebug.h"
#include "gdcmPixelWriteConvert.h"
#include "gdcmFile.h"
#include "gdcmUtil.h"

#include <vector>
#include <stdlib.h>

#define WITHOFFSETTABLE 0

namespace gdcm
{
//-----------------------------------------------------------------------------
// Constructor / Destructor
/**
 * \brief Constructor
 */
PixelWriteConvert::PixelWriteConvert()
{
   ReadData     = 0;
   ReadDataSize = 0;

   UserData     = 0;
   UserDataSize = 0;
   Compressed   = false;
}

/**
 * \brief Destructor
 */
PixelWriteConvert::~PixelWriteConvert()
{
   if( Compressed )
     {
     delete[] UserData;
     }
}

//-----------------------------------------------------------------------------
// Public
/**
 * \brief   sets Read Data (and size)
 * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
 *                     just cast the pointer for calling the method)
 * @param   size data size, in bytes
 */
void PixelWriteConvert::SetReadData(uint8_t *data, size_t size)
{
   ReadData = data;
   ReadDataSize = size;
}

/**
 * \brief   Sets the internal pointer to the caller's inData
 *          image representation, BUT WITHOUT COPYING THE DATA.
 *          - 'image' Pixels are presented as C-like 2D arrays : line per line.
 *          - 'volume'Pixels are presented as C-like 3D arrays : plane per plane
 * \warning Since the pixels are not copied, it is the caller's responsability
 *          not to deallocate its data before gdcm uses them (e.g. with
 *          the Write() method )
 * @param   data data (uint8_t is for prototyping. if your data is not uint8_t
 *                     just cast the pointer for calling the method)
 * @param   size size, in bytes.
 */
void PixelWriteConvert::SetUserData(uint8_t *data, size_t size)
{
   UserData = data;
   UserDataSize = size;
}

/**
 * \brief   Get Data (UserData or ReadData)
 * @return  data (uint8_t is for prototyping. if your data is
 *                    *not* uint8_t, just cast the returned pointer)
 */
uint8_t *PixelWriteConvert::GetData()
{
   if ( UserData )
   {
      return UserData;
   }
   else
   {
      return ReadData;
   }
}

/**
 * \brief   Get Data Size (UserData or ReadData)
 * @return  size, in bytes.
 */
size_t PixelWriteConvert::GetDataSize()
{
   if ( UserData )
   {
      return UserDataSize;
   }
   else
   {
      return ReadDataSize;
   }
}

typedef std::pair<size_t, uint32_t> JpegPair; //offset, jpeg size
typedef std::vector<JpegPair> JpegVector;

bool gdcm_write_JPEG2000_file (std::ostream *of, char *inputdata, size_t inputlength,
  int image_width, int image_height, int numZ, int sample_pixel, int bitsallocated,
  int sign, int quality, size_t &);

void WriteDICOMItems(std::ostream *fp, JpegVector &v)
{
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t dummy = 0x12345678;
  size_t offset = fp->tellp();
  JpegPair jp;
  jp.first = offset;
  v.push_back(jp);
  gdcm::binary_write(*fp, dummy);
}

// PS 3.5, page 66
#ifdef NDEBUG
void EncodeWithoutBasicOffsetTable(std::ostream *fp, int)// JpegVector& v) //, uint32_t length)
{
#else
void EncodeWithoutBasicOffsetTable(std::ostream *fp, int numFrag)// JpegVector& v) //, uint32_t length)
{
  assert( numFrag == 1);
#endif
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t item_length = 0x0000;
  gdcm::binary_write(*fp, item_length);

}

// PS 3.5, page 67
void EncodeWithBasicOffsetTable(std::ostream *fp, int numFrag, size_t &start)
{
  // Item tag:
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe000;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);
  // Item Length
  uint32_t item_length = numFrag*4; // sizeof(uint32_t)
  gdcm::binary_write(*fp, item_length);

  // Just prepare the space
  start = fp->tellp(); //to be able to rewind
  for(int i=0; i<numFrag;++i)
    {
    uint32_t dummy = 0x0000;
    gdcm::binary_write(*fp, dummy);
    }
}

void UpdateBasicOffsetTable(std::ostream *fp, JpegVector const &v, size_t pos)
{
  JpegVector::const_iterator i;
  fp->seekp( pos );
  const JpegPair &first = v[0];
  for(i=v.begin(); i!=v.end(); ++i)
    {
    const JpegPair &jp = *i;
    if(i == v.begin() ){ assert( jp.first - first.first == 0); }
    uint32_t offset = (uint32_t)(jp.first - first.first);
    gdcm::binary_write(*fp, offset);
    //std::cerr << "Updating Table:" << jp.first - first.first << std::endl;
    }
}

void UpdateJpegFragmentSize(std::ostream *fp, JpegVector const &v)
{
  JpegVector::const_iterator i;
  for(i= v.begin(); i!=v.end(); ++i)
    {
    const JpegPair &jp = *i;
    fp->seekp( jp.first );
    uint32_t length = jp.second;
    gdcm::binary_write(*fp, length);
    //std::cerr << "Updating:" << jp.first << "," << jp.second << std::endl;
    }
}

void CloseJpeg(std::ostream *fp, JpegVector &v)
{
  // sequence terminator
  uint16_t group = 0xfffe;
  uint16_t elem  = 0xe0dd;
  gdcm::binary_write(*fp, group);
  gdcm::binary_write(*fp, elem);

  uint32_t length = 0x0;
  gdcm::binary_write(*fp, length);

  // Jpeg is done, now update the frag length
  UpdateJpegFragmentSize(fp, v);
}

// I need to pass the File*. I do not understand how PixelWriteConvert is supposed
// to access this information otherwise
// size can now be computer from File attributes (what an API...)
void PixelWriteConvert::SetCompressJPEG2000UserData(uint8_t *data, size_t size, File *image)
{
  Compressed = true;
  //char * userData = reinterpret_cast<char*>(UserData);

   itksys_ios::stringstream *of = new itksys_ios::stringstream(); // ostringstream: debug / sunos
    int xsize = image->GetXSize();
   int ysize = image->GetYSize();
  int zsize =  image->GetZSize();
    int samplesPerPixel = image->GetSamplesPerPixel();
   //std::cout << "X: " << xsize << std::endl;
   //std::cout << "Y: " << ysize << std::endl;
   //std::cout << "Sample: " << samplesPerPixel << std::endl;
    int bitsallocated = image->GetBitsAllocated();
    int sign = image->IsSignedPixelData();
   unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
    assert( fragment_size*zsize == size );

   JpegVector JpegFragmentSize;
#if WITHOFFSETTABLE
   size_t bots; //basic offset table start
   EncodeWithBasicOffsetTable(of, zsize, bots);
#else
   EncodeWithoutBasicOffsetTable(of, 1);
#endif
   uint8_t *pImageData = data;
   for(int i=0; i<zsize;i++)
     {
     WriteDICOMItems(of, JpegFragmentSize);
     size_t beg = of->tellp();
     size_t nbbytes = 0;
     gdcm_write_JPEG2000_file(of, (char*)pImageData,size,
       image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
       image->GetBitsAllocated(), sign, 100, nbbytes);
     //userData, UserDataSize);
     //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
     //       samplesPerPixel, quality, JpegFragmentSize);
     //assert( !(fragment_size % 2) );
     // Update the JpegVector with offset
     size_t end = of->tellp();
     if( (end - beg) != nbbytes )
       {
       gdcmErrorMacro( "Go fix your compiler implementation of stringstream." );
       UserData = 0;
       UserDataSize = 0;
       return;
       }
     //static int i = 0;
     JpegPair &jp = JpegFragmentSize[i];
     jp.second = (uint32_t)(end-beg);
     if( ((end-beg) % 2) )
       {
       of->put( '\0' );
       jp.second += 1;
       }
     assert( !(jp.second % 2) );
     //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
     pImageData += fragment_size;
     }
   CloseJpeg(of, JpegFragmentSize);
#if WITHOFFSETTABLE
   UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
#endif

   size_t of_size = of->str().size();
   gdcmErrorMacro( "Of_size: " << of_size ); // FIXME: remote debug of sunos
   UserData = new uint8_t[of_size];
   memcpy(UserData, of->str().c_str(), of_size);
   UserDataSize = of_size;
   delete of;
}

bool gdcm_write_JPEG_file8 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);
bool gdcm_write_JPEG_file12 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);
bool gdcm_write_JPEG_file16 (std::ostream *fp, char *inputdata, size_t inputlength,
                           int image_width, int image_height, int numZ,
                           int sample_pixel, int bitsallocated, int quality);

void PixelWriteConvert::SetCompressJPEGUserData(uint8_t *data, size_t size, File *image)
{
  (void)data;
  (void)size;
  (void)image;
  Compressed = true;
  //char * userData = reinterpret_cast<char*>(UserData);

   itksys_ios::ostringstream *of = new itksys_ios::ostringstream();
    int xsize = image->GetXSize();
   int ysize = image->GetYSize();
  int zsize =  image->GetZSize();
    int samplesPerPixel = image->GetSamplesPerPixel();
   //std::cout << "X: " << xsize << std::endl;
   //std::cout << "Y: " << ysize << std::endl;
   //std::cout << "Sample: " << samplesPerPixel << std::endl;
    int bitsallocated = image->GetBitsAllocated();
   unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
    assert( fragment_size*zsize == size );

   JpegVector JpegFragmentSize;
#if WITHOFFSETTABLE
   size_t bots; //basic offset table start
   EncodeWithBasicOffsetTable(of, zsize, bots);
#else
   EncodeWithoutBasicOffsetTable(of, 1);
#endif
   uint8_t *pImageData = data;
   for(int i=0; i<zsize;i++)
     {
     WriteDICOMItems(of, JpegFragmentSize);
  size_t beg = of->tellp();
     if( bitsallocated == 8 )
       {
       gdcm_write_JPEG_file8(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100 );
       }
     else if (bitsallocated <= 12)
       {
       assert( bitsallocated >= 8 );
       gdcm_write_JPEG_file12(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100);
       }
     else if (bitsallocated <= 16)
       {
       assert( bitsallocated >= 12 );
       gdcm_write_JPEG_file16(of, (char*)pImageData,size,
         image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
         image->GetBitsAllocated(), 100);
       }
     else
       {
       abort();
       }
    size_t end = of->tellp();
    //static int i = 0;
    JpegPair &jp = JpegFragmentSize[i];
      jp.second = (uint32_t)(end-beg);
    if( ((end-beg) % 2) )
      {
      of->put( '\0' );
      jp.second += 1;
      }
    assert( !(jp.second % 2) );
    //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
    //++i;

  //JpegPair &jp = v[0];
  //jp.second = 15328;

     //userData, UserDataSize);
     //     CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
     //       samplesPerPixel, quality, JpegFragmentSize);
     //assert( !(fragment_size % 2) );
     pImageData += fragment_size;
     }
   CloseJpeg(of, JpegFragmentSize);
#if WITHOFFSETTABLE
   UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
#endif

   size_t of_size = of->str().size();
   UserData = new uint8_t[of_size];
   memcpy(UserData, of->str().c_str(), of_size);
   UserDataSize = of_size;
   delete of;
}

//-----------------------------------------------------------------------------
// Protected
//bool PixelWriteConvert::CompressJPEG2000(uint8_t *data, size_t size)
//{
//}

//-----------------------------------------------------------------------------
// Private

//-----------------------------------------------------------------------------
// Print

//-----------------------------------------------------------------------------
} // end namespace gdcm
<|endoftext|>


<commit_before>#include "control.hpp"
#include "math.hpp"
#include "model.hpp"

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GL/glu.h>
#include <SOIL/SOIL.h>

#include <chrono>
#include <cmath>
#include <iostream>
#include <memory>
#include <sstream>

#include "omp.h"

namespace Control {

	void control::deadzone(float &x, float &y) {
		float r = sqrt(x*x + y*y), theta = atan2(y, x);
		if(r < r_dead) {
			x = y = 0;
		} else {
			x = (x/r - r_dead) / (1-r_dead);
			y = (y/r - r_dead) / (1-r_dead);
		}
	}

	bool control::init(std::atomic_bool &alive) {
		//using namespace Util;
		// Task 1: view before model (splash)
		using namespace View;
		glfwSetInputMode(viewer.win, GLFW_STICKY_KEYS, 1);
		glfwMakeContextCurrent(viewer.win);

		using namespace Model;
		mesh_t mesh(10, 10,
		[](float s, float t, std::vector<float> &vertices) {
			using namespace Math;
			auto theta = s*M_PI*2, phi = t*M_PI;
			vertices.emplace_back(cos(theta)*sin(phi)); // X
			vertices.emplace_back(sin(theta)*sin(phi)); // Y
			vertices.emplace_back(cos(phi));            // Z
		});

		glGenBuffers(1, &viewer.ids[view::e_id_vbuf]);
		glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
		glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float),
			(void*) &mesh.vertices[0], GL_STATIC_DRAW);

		glGenBuffers(1, &viewer.ids[view::e_id_fbuf]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
			viewer.ids[view::e_id_fbuf]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.faces.size() * sizeof(int),
			(void*) &mesh.faces[0], GL_STATIC_DRAW);
		viewer.nTriangles = mesh.faces.size()/3;
		return alive;
		/*
		// Task 2: wavefront obj model loading
		using namespace Model;
		obj_t object;
		auto status = obj_t::load(this -> mpath, object);
		if(status != obj_t::e_ok) {
			errors.push_back("The model failed to load.");
			return alive = false;
		}

		glGenBuffers(view::e_id_model-view::e_id_vbuf,
				&viewer.ids[view::e_id_vbuf]);
		// TODO Use more than first range of each obj type
		if(object.v_beg.size() == 0) {
			errors.push_back("The loaded model does not contain vertices.");
			return alive = false;
		} else {
			auto v0 = object.v_beg[0], v1 = object.v_end[0];
			glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
			glBufferData(GL_ARRAY_BUFFER, v1-v0,
					(void*)(&object.floats[v0]), GL_STATIC_DRAW);
		}
		if(object.vn_beg.size() != 0) {
			auto vn0 = object.vn_beg[0], vn1 = object.vn_end[0];
			glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vnbuf]);
			glBufferData(GL_ARRAY_BUFFER, vn1-vn0,
					(void*)(&object.floats[vn0]), GL_STATIC_DRAW);
		}
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
				viewer.ids[view::e_id_fbuf]);
		int f0, f1;
		if(object.f0_beg.size() != 0) {
			f0 = object.f0_beg[0];
			f1 = object.f0_end[0];
		} else if(object.f1_beg.size() != 0) {
			f0 = object.f1_beg[0];
			f1 = object.f1_end[0];
		} else if(object.f2_beg.size() != 0) {
			f0 = object.f2_beg[0];
			f1 = object.f2_end[0];
		} else if(object.f3_beg.size() != 0) {
			f0 = object.f3_beg[0];
			f1 = object.f3_end[0];
		} else {
			errors.push_back("The loaded model does not contain faces.");
			return alive = false;
		}
		viewer.nTriangles = (f1-f0)/3;
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, viewer.nTriangles*3,
				(void*)(&object.ints[f0]), GL_STATIC_DRAW);*/

		/*
		// Task 2: model loading
		using namespace Model::Ply;
 		Header model(this -> mpath);
 		if(model.status) {
			std::cout << model.statusContext << std::endl;
			return;
		}
		auto start = begin(model.elements),
				 stop = end(model.elements);
		auto vertices = std::find_if(start, stop,
			[](Element const& el) -> bool {
				// TODO Change behavior based on the number of elements
				return el.name == "vertex" && !el.has_list;
		}), indices = std::find_if(start, stop,
			[](Element const& el) -> bool {
				int sz = el.properties.size();
				return el.name == "face"
					&& (el.has_list ? sz==1 : sz==3);
		});
		if(vertices == stop || indices == stop) {
			std::cout << "The model is valid, but does not match "
				"the anticipated structure." << std::endl;
			return;
		Task 3: view after model
		glGenBuffers(1, &viewer.ids[view::e_id_vbuf]);
		glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
		glBufferData(GL_ARRAY_BUFFER, vertices->data.size(),
				(void*)(&vertices->data[0]), GL_STATIC_DRAW);

		glGenBuffers(1, &viewer.ids[view::e_id_ibuf]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
				viewer.ids[view::e_id_ibuf]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices->data.size(),
				(void*)(&indices->data[0]), GL_STATIC_DRAW);
		viewer.nTriangles = indices -> instances;
		glUseProgram(viewer.ids[view::e_id_prog]);*/
	}

	bool control::poll(std::atomic_bool &alive) {
		std::ostringstream oss;
		if(alive)
			viewer.poll(alive);
		if(alive) {
			if(glfwGetKey(viewer.win, GLFW_KEY_ESCAPE) == GLFW_PRESS)
				alive = false;
			int nButtons;
			const uint8_t *buttons =
				glfwGetJoystickButtons(GLFW_JOYSTICK_1, &nButtons);
			for(int i = 0; i < nButtons; i++) {
				if(buttons[i] == GLFW_PRESS) {
					if(i == 8) alive = false;
					else if(i == 7)
						viewer.x = viewer.y = viewer.z = 0;
					else if(i == 4)
						viewer.z -= .1;
					else if(i == 5)
						viewer.z += .1;
					else std::cout << i << std::endl;
				}
			}
			int nAxes;
			const GLfloat *axes =
				glfwGetJoystickAxes(GLFW_JOYSTICK_1, &nAxes);
			if(nAxes >= 2) {
				float x = axes[0], y = axes[1];
				deadzone(x, y);
				viewer.x += x/10;
				viewer.y -= y/10;
			}
			if(nAxes >= 5) {
				float theta = axes[3], phi = axes[4];
				viewer.theta = theta*M_PI;
				viewer.phi = phi*M_PI;
			}
		}
		return alive;
	}

	bool control::run(std::atomic_bool &alive) {
		using namespace Model::Ply;
		auto delay = std::chrono::milliseconds(150);

		int frame = 0, dFrames = 0, fps = 0;
		auto kill = [&alive] {alive = false;};
		double t1 = glfwGetTime(), t2;
		while(true) {
			if(poll(alive)) viewer.run(alive);
			t2 = glfwGetTime() - t1;
			frame++;
			dFrames++;
			if(t2 > 1.0) {
				std::cout << "FPS: " << int(dFrames/t2) << std::endl;
				t1 += t2;
				dFrames = 0;
			}
			if(!alive) break;
		}
		return alive;
	}

	control::control(std::atomic_bool &alive, const char *mpath):
		task(), mpath(mpath), viewer(alive) {}
}
<commit_msg>Changed bindings<commit_after>#include "control.hpp"
#include "math.hpp"
#include "model.hpp"

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GL/glu.h>
#include <SOIL/SOIL.h>

#include <chrono>
#include <cmath>
#include <iostream>
#include <memory>
#include <sstream>

#include "omp.h"

namespace Control {

	void control::deadzone(float &x, float &y) {
		float r = sqrt(x*x + y*y), theta = atan2(y, x);
		if(r < r_dead) {
			x = y = 0;
		} else {
			x = (x/r - r_dead) / (1-r_dead);
			y = (y/r - r_dead) / (1-r_dead);
		}
	}

	bool control::init(std::atomic_bool &alive) {
		//using namespace Util;
		// Task 1: view before model (splash)
		using namespace View;
		glfwSetInputMode(viewer.win, GLFW_STICKY_KEYS, 1);
		glfwMakeContextCurrent(viewer.win);

		using namespace Model;
		mesh_t mesh(10, 10,
		[](float s, float t, std::vector<float> &vertices) {
			using namespace Math;
			auto theta = s*M_PI*2, phi = t*M_PI;
			vertices.emplace_back(cos(theta)*sin(phi)); // X
			vertices.emplace_back(sin(theta)*sin(phi)); // Y
			vertices.emplace_back(cos(phi));            // Z
		});

		glGenBuffers(1, &viewer.ids[view::e_id_vbuf]);
		glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
		glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float),
			(void*) &mesh.vertices[0], GL_STATIC_DRAW);

		glGenBuffers(1, &viewer.ids[view::e_id_fbuf]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
			viewer.ids[view::e_id_fbuf]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.faces.size() * sizeof(int),
			(void*) &mesh.faces[0], GL_STATIC_DRAW);
		viewer.nTriangles = mesh.faces.size()/3;
		return alive;
		/*
		// Task 2: wavefront obj model loading
		using namespace Model;
		obj_t object;
		auto status = obj_t::load(this -> mpath, object);
		if(status != obj_t::e_ok) {
			errors.push_back("The model failed to load.");
			return alive = false;
		}

		glGenBuffers(view::e_id_model-view::e_id_vbuf,
				&viewer.ids[view::e_id_vbuf]);
		// TODO Use more than first range of each obj type
		if(object.v_beg.size() == 0) {
			errors.push_back("The loaded model does not contain vertices.");
			return alive = false;
		} else {
			auto v0 = object.v_beg[0], v1 = object.v_end[0];
			glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
			glBufferData(GL_ARRAY_BUFFER, v1-v0,
					(void*)(&object.floats[v0]), GL_STATIC_DRAW);
		}
		if(object.vn_beg.size() != 0) {
			auto vn0 = object.vn_beg[0], vn1 = object.vn_end[0];
			glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vnbuf]);
			glBufferData(GL_ARRAY_BUFFER, vn1-vn0,
					(void*)(&object.floats[vn0]), GL_STATIC_DRAW);
		}
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
				viewer.ids[view::e_id_fbuf]);
		int f0, f1;
		if(object.f0_beg.size() != 0) {
			f0 = object.f0_beg[0];
			f1 = object.f0_end[0];
		} else if(object.f1_beg.size() != 0) {
			f0 = object.f1_beg[0];
			f1 = object.f1_end[0];
		} else if(object.f2_beg.size() != 0) {
			f0 = object.f2_beg[0];
			f1 = object.f2_end[0];
		} else if(object.f3_beg.size() != 0) {
			f0 = object.f3_beg[0];
			f1 = object.f3_end[0];
		} else {
			errors.push_back("The loaded model does not contain faces.");
			return alive = false;
		}
		viewer.nTriangles = (f1-f0)/3;
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, viewer.nTriangles*3,
				(void*)(&object.ints[f0]), GL_STATIC_DRAW);*/

		/*
		// Task 2: model loading
		using namespace Model::Ply;
 		Header model(this -> mpath);
 		if(model.status) {
			std::cout << model.statusContext << std::endl;
			return;
		}
		auto start = begin(model.elements),
				 stop = end(model.elements);
		auto vertices = std::find_if(start, stop,
			[](Element const& el) -> bool {
				// TODO Change behavior based on the number of elements
				return el.name == "vertex" && !el.has_list;
		}), indices = std::find_if(start, stop,
			[](Element const& el) -> bool {
				int sz = el.properties.size();
				return el.name == "face"
					&& (el.has_list ? sz==1 : sz==3);
		});
		if(vertices == stop || indices == stop) {
			std::cout << "The model is valid, but does not match "
				"the anticipated structure." << std::endl;
			return;
		Task 3: view after model
		glGenBuffers(1, &viewer.ids[view::e_id_vbuf]);
		glBindBuffer(GL_ARRAY_BUFFER, viewer.ids[view::e_id_vbuf]);
		glBufferData(GL_ARRAY_BUFFER, vertices->data.size(),
				(void*)(&vertices->data[0]), GL_STATIC_DRAW);

		glGenBuffers(1, &viewer.ids[view::e_id_ibuf]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
				viewer.ids[view::e_id_ibuf]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices->data.size(),
				(void*)(&indices->data[0]), GL_STATIC_DRAW);
		viewer.nTriangles = indices -> instances;
		glUseProgram(viewer.ids[view::e_id_prog]);*/
	}

	bool control::poll(std::atomic_bool &alive) {
		std::ostringstream oss;
		if(alive)
			viewer.poll(alive);
		if(alive) {
			if(glfwGetKey(viewer.win, GLFW_KEY_ESCAPE) == GLFW_PRESS)
				alive = false;
			int nButtons;
			const uint8_t *buttons =
				glfwGetJoystickButtons(GLFW_JOYSTICK_1, &nButtons);
			for(int i = 0; i < nButtons; i++) {
				if(buttons[i] == GLFW_PRESS) {
					if(i == 8) alive = false;
					else if(i == 7)
						viewer.x = viewer.y = viewer.z = 0;
					else if(i == 4)
						viewer.y -= .1;
					else if(i == 5)
						viewer.y += .1;
					else std::cout << i << std::endl;
				}
			}
			int nAxes;
			const GLfloat *axes =
				glfwGetJoystickAxes(GLFW_JOYSTICK_1, &nAxes);
			if(nAxes >= 2) {
				float x = axes[0], y = axes[1];
				deadzone(x, y);
				viewer.x -= x/20;
				viewer.z -= y/20;
			}
			if(nAxes >= 5) {
				float theta = axes[3], phi = axes[4];
				viewer.theta = theta*M_PI;
				viewer.phi = phi*M_PI;
			}
		}
		return alive;
	}

	bool control::run(std::atomic_bool &alive) {
		using namespace Model::Ply;
		auto delay = std::chrono::milliseconds(150);

		int frame = 0, dFrames = 0, fps = 0;
		auto kill = [&alive] {alive = false;};
		double t1 = glfwGetTime(), t2;
		while(true) {
			if(poll(alive)) viewer.run(alive);
			t2 = glfwGetTime() - t1;
			frame++;
			dFrames++;
			if(t2 > 1.0) {
				std::cout << "FPS: " << int(dFrames/t2) << std::endl;
				t1 += t2;
				dFrames = 0;
			}
			if(!alive) break;
		}
		return alive;
	}

	control::control(std::atomic_bool &alive, const char *mpath):
		task(), mpath(mpath), viewer(alive) {}
}
<|endoftext|>


<commit_before>#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
#include <utility>
#include <optional>
#include <map>

#include "core/argparse.h"
#include "core/io.h"
#include "core/image.h"
#include "core/cint.h"
#include "core/image_draw.h"
#include "core/minmax.h"
#include "core/timepoint.h"

using namespace euphoria::core;

struct ImageAndFile
{
    ImageAndFile() {}

    ImageAndFile(const std::string& f, const Image& i)
        : file(f)
        , image(i)
    {}

    std::string file;
    Image image;
};

std::vector<ImageAndFile>
LoadImages(const std::vector<std::string>& files)
{
    auto images = std::vector<ImageAndFile>{};

    for(const auto& f: files)
    {
        auto chunk = io::FileToChunk(f);
        if(chunk == nullptr)
        {
            std::cerr << "failed to read image file " << f << "\n";
            return {};
        }
        auto loaded_image = LoadImage(chunk, f, AlphaLoad::Keep);
        if(loaded_image.error.empty() == false)
        {
            std::cerr << "failed to read image data " <<
                loaded_image.error << "\n";
            return {};
        }

        images.emplace_back(f, loaded_image.image);
    }

    return images;
}

struct ExtractedColor
{
    ExtractedColor(const Rgbi& r, int c)
        : color(r)
        , count(c)
    {
    }

    Rgbi color;
    int count;
};

enum class SortRange
{
    R, G, B
};

float
GetValue(SortRange range, const Rgbi& c)
{
    switch(range)
    {
        case SortRange::R: return rgb(c).r;
        case SortRange::G: return rgb(c).g;
        case SortRange::B: return rgb(c).b;
        default: return 0;
    }
}

std::tuple<SortRange, Range<float>>
FindGreatestSortRange(const std::vector<Rgbi>& colors)
{
    using Tu = std::tuple<SortRange, Range<float>>;

    // todo(Gustav): this iterates 3 times through the color array, make it 1
    // todo(Gustav): remove the std::function and use GetValue(SortRange)
    auto make = [&]
    (SortRange r, std::function<float (const Rgbi& c)> conv) -> Tu
    {
        return std::make_tuple(r, FindMinMaxRange<float>(colors, conv));
    };
    auto ranges = std::vector<Tu>
    {
        make(SortRange::R, [](const Rgbi& c) -> float { return rgb(c).r; }),
        make(SortRange::G, [](const Rgbi& c) -> float { return rgb(c).g; }),
        make(SortRange::B, [](const Rgbi& c) -> float { return rgb(c).b; })
    };
    std::sort(ranges.begin(), ranges.end(), [](const Tu& lhs, const Tu& rhs)
    {
        return std::get<1>(lhs).GetRange() > std::get<1>(rhs).GetRange();
    });
    return ranges[0];
}

void
Sort(SortRange sort_range, std::vector<Rgbi>* colors)
{
    auto sort = [&](std::function<float (const Rgbi& c)> conv)
    {
        std::sort(colors->begin(), colors->end(), [&]
        (
            const Rgbi& lhs, const Rgbi& rhs)
            {
                return conv(lhs) < conv(rhs);
            }
        );
    };

    switch(sort_range)
    {
    case SortRange::R: sort([](const Rgbi& c) -> float { return c.r; }); break;
    case SortRange::G: sort([](const Rgbi& c) -> float { return c.g; }); break;
    case SortRange::B: sort([](const Rgbi& c) -> float { return c.b; }); break;
    }
}

size_t
FindMedianIndex(SortRange sort, const std::vector<Rgbi>& colors, Range<float> range)
{
    const auto median = range.GetRange()/2 + range.lower_bound;
    // todo(Gustav): make non-linear
    for(size_t index = 0; index<colors.size()-1; index+=1)
    {
        if(GetValue(sort, colors[index+1]) >= median)
        {
            return index;
        }
    }
    DIE("shouldn't happen...");
    return 0;
}

std::vector<Rgbi>
MedianCut(const std::vector<Rgbi>& src, int depth)
{
    if(src.empty())
    {
        return {};
    }

    if(depth <= 0 || src.size() < 2)
    {
        auto sum = Rgb{0,0,0};
        float total = 0;
        for(const auto& c: src)
        {
            sum += rgb(c);
            total += 1;
        }

        const auto r = rgbi(sum / total);

        return {r};
    }

    const auto [range, rrange] = FindGreatestSortRange(src);

    using V = std::vector<Rgbi>;
    V vec = src;
    Sort(range, &vec);

    // const auto median_index = vec.size() / 2;
    const auto median_index = FindMedianIndex(range, vec, rrange);
    const auto median = vec.begin()+median_index;
    const auto left = V(vec.begin(), median);
    const auto right = V(median, vec.end());

    auto ret = MedianCut(left, depth -1);
    const auto rhs = MedianCut(right, depth-1);
    ret.insert(ret.end(), rhs.begin(), rhs.end());

    return ret;
}

int
Find(std::vector<ExtractedColor>* psource, const Rgbi& color, float length)
{
    auto& source = *psource;

    for(int i=0; i<source.size(); i+=1)
    {
        if((rgb(source[i].color) - rgb(color)).GetLength()*255 < length)
        {
            return i;
        }
    }

    source.emplace_back(color, 0);
    return source.size() - 1;
}

std::vector<ExtractedColor>
ExtractColors(const std::vector<ImageAndFile>& images, float range)
{
    auto ret = std::vector<ExtractedColor>{};

    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto index = Find(&ret, rgbi(img.image.GetPixel(x,y)), range);
            ret[index].count += 1;
        }
    }

    return ret;
}

std::vector<ExtractedColor>
ExtractColors(const std::vector<ImageAndFile>& images)
{
    std::map<int, int> colors;
    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto color = rgbi(img.image.GetPixel(x, y));
            const auto hex = color.ToHex();
            auto val = std::get<0>(colors.try_emplace(hex, 0));
            val->second += 1;
        }
    }

    auto ret = std::vector<ExtractedColor>{};
    for(const auto c: colors)
    {
        ret.emplace_back(Rgbi::FromHex(c.first), c.second);
    }
    return ret;
}

std::vector<Rgbi>
ExtractAllColors(const std::vector<ImageAndFile>& images)
{
    auto ret = std::vector<Rgbi>{};
    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto color = rgbi(img.image.GetPixel(x, y));
            ret.emplace_back(color);
        }
    }

    return ret;
}

bool
HandleImage
(
    const std::vector<std::string>& files,
    int depth,
    int image_size,
    const std::string& file
)
{
    // load images
    const auto images = LoadImages(files);
    if(images.empty())
    {
        return false;
    }

    const auto start = Now();

    // extract colors
    auto colors = MedianCut(ExtractAllColors(images), depth);

    const auto end = Now();

    const auto seconds = SecondsBetween(start, end);
    std::cout << "Palette extraction took " << seconds << " seconds.\n";

    if(colors.empty())
    {
        return false;
    }

    Image image;
    image.SetupNoAlphaSupport(image_size * colors.size(), image_size);
    for
    (
        int i = 0;
        i < Csizet_to_int(colors.size());
        i += 1
    )
    {
        DrawSquare
        (
            &image,
            colors[i],
            image_size * i,
            image_size - 1,
            image_size
        );
    }

    io::ChunkToFile(image.Write(ImageWriteFormat::PNG), file);
    return true;
}

bool
HandlePrint
(
    const std::vector<std::string>& files,
    int depth
)
{
    // load images
    const auto images = LoadImages(files);
    if(images.empty())
    {
        return false;
    }

    // extract colors
    auto colors = MedianCut(ExtractAllColors(images), depth);

    std::sort
    (
        colors.begin(),
        colors.end(),
        [](const Rgbi& lhs, const Rgbi& rhs) -> bool
        {
            return rgb(lhs).CalcLuminance() < rgb(rhs).CalcLuminance();
        }
    );

    if(colors.empty())
    {
        return false;
    }

    // print colors
    for(auto c: colors)
    {
        std::cout << c << "\n";
    }

    std::cout << "Found #colors: " << colors.size() << "\n";
    return true;
}

int
main(int argc, char* argv[])
{
    auto parser = argparse::Parser
    {
        "extract colors from images"
    };
    auto subs = parser.AddSubParsers();

    subs->Add
    (
        "print", "print all colors found in images",
        [](argparse::SubParser* sub)
        {
            std::vector<std::string> files;
            int depth = 3;

            sub->Add("--depth", &depth)
                .AllowBeforePositionals()
                .Nargs("R")
                .Help("change the palette depth")
                ;
            sub->AddVector("files", &files)
                .Nargs("F")
                .Help("the files to analyze")
                ;

            return sub->OnComplete([&]
            {
                const auto was_extracted = HandlePrint
                (
                    files, depth
                );

                return was_extracted
                    ? argparse::ParseResult::Ok
                    : argparse::ParseResult::Error
                    ;
            });
        }
    );

    subs->Add
    (
        "image", "write all colors found in images to a palette image",
        [](argparse::SubParser* sub)
        {
            int image_size = 5;
            std::string file = "pal.png";
            std::vector<std::string> files;
            int depth = 3;

            sub->Add("--depth", &depth)
                .AllowBeforePositionals()
                .Nargs("R")
                .Help("change the palette depth")
                ;
            sub->Add("--size", &image_size)
                .AllowBeforePositionals()
                .Nargs("S")
                .Help("change the image size")
                ;
            sub->Add("-o, --output", &file)
                .AllowBeforePositionals()
                .Nargs("FILE")
                .Help("change where to save the output")
                ;
            sub->AddVector("files", &files)
                .Nargs("F")
                .Help("the files to analyze")
                ;

            return sub->OnComplete([&]
            {
                const auto was_extracted = HandleImage
                (
                    files,
                    depth,
                    image_size,
                    file
                );

                return was_extracted
                    ? argparse::ParseResult::Ok
                    : argparse::ParseResult::Error
                    ;
            });
        }
    );

    return ParseFromMain(&parser, argc, argv);
}
<commit_msg>changed to subvec to have less copying<commit_after>#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
#include <utility>
#include <optional>
#include <map>

#include "core/argparse.h"
#include "core/io.h"
#include "core/image.h"
#include "core/cint.h"
#include "core/image_draw.h"
#include "core/minmax.h"
#include "core/timepoint.h"
#include "core/subvec.h"

using namespace euphoria::core;

struct ImageAndFile
{
    ImageAndFile() {}

    ImageAndFile(const std::string& f, const Image& i)
        : file(f)
        , image(i)
    {}

    std::string file;
    Image image;
};

std::vector<ImageAndFile>
LoadImages(const std::vector<std::string>& files)
{
    auto images = std::vector<ImageAndFile>{};

    for(const auto& f: files)
    {
        auto chunk = io::FileToChunk(f);
        if(chunk == nullptr)
        {
            std::cerr << "failed to read image file " << f << "\n";
            return {};
        }
        auto loaded_image = LoadImage(chunk, f, AlphaLoad::Keep);
        if(loaded_image.error.empty() == false)
        {
            std::cerr << "failed to read image data " <<
                loaded_image.error << "\n";
            return {};
        }

        images.emplace_back(f, loaded_image.image);
    }

    return images;
}

struct ExtractedColor
{
    ExtractedColor(const Rgbi& r, int c)
        : color(r)
        , count(c)
    {
    }

    Rgbi color;
    int count;
};

enum class SortRange
{
    R, G, B
};

float
GetValue(SortRange range, const Rgbi& c)
{
    switch(range)
    {
        case SortRange::R: return rgb(c).r;
        case SortRange::G: return rgb(c).g;
        case SortRange::B: return rgb(c).b;
        default: return 0;
    }
}

std::tuple<SortRange, Range<float>>
FindGreatestSortRange(SubVec<Rgbi> colors)
{
    using Tu = std::tuple<SortRange, Range<float>>;

    // todo(Gustav): this iterates 3 times through the color array, make it 1
    // todo(Gustav): remove the std::function and use GetValue(SortRange)
    auto make = [&]
    (SortRange r, std::function<float (const Rgbi& c)> conv) -> Tu
    {
        return std::make_tuple(r, FindMinMaxRange<float>(colors, conv));
    };
    auto ranges = std::vector<Tu>
    {
        make(SortRange::R, [](const Rgbi& c) -> float { return rgb(c).r; }),
        make(SortRange::G, [](const Rgbi& c) -> float { return rgb(c).g; }),
        make(SortRange::B, [](const Rgbi& c) -> float { return rgb(c).b; })
    };
    std::sort(ranges.begin(), ranges.end(), [](const Tu& lhs, const Tu& rhs)
    {
        return std::get<1>(lhs).GetRange() > std::get<1>(rhs).GetRange();
    });
    return ranges[0];
}

void
Sort(SortRange sort_range, SubVec<Rgbi> colors)
{
    auto sort = [&](std::function<float (const Rgbi& c)> conv)
    {
        std::sort(colors.begin(), colors.end(), [&]
        (
            const Rgbi& lhs, const Rgbi& rhs)
            {
                return conv(lhs) < conv(rhs);
            }
        );
    };

    switch(sort_range)
    {
    case SortRange::R: sort([](const Rgbi& c) -> float { return c.r; }); break;
    case SortRange::G: sort([](const Rgbi& c) -> float { return c.g; }); break;
    case SortRange::B: sort([](const Rgbi& c) -> float { return c.b; }); break;
    }
}

size_t
FindMedianIndex(SortRange sort, SubVec<Rgbi> colors, Range<float> range)
{
    const auto median = range.GetRange()/2 + range.lower_bound;
    // todo(Gustav): make non-linear
    for(size_t index = 0; index<colors.size()-1; index+=1)
    {
        if(GetValue(sort, colors[index+1]) >= median)
        {
            return index;
        }
    }
    DIE("shouldn't happen...");
    return 0;
}

std::vector<Rgbi>
MedianCut(SubVec<Rgbi> src, int depth)
{
    if(src.empty())
    {
        return {};
    }

    if(depth <= 0 || src.size() < 2)
    {
        auto sum = Rgb{0,0,0};
        float total = 0;
        for(const auto& c: src)
        {
            sum += rgb(c);
            total += 1;
        }

        const auto r = rgbi(sum / total);

        return {r};
    }

    const auto [range, rrange] = FindGreatestSortRange(src);

    Sort(range, src);

    // const auto median_index = src.size() / 2;
    const auto median = FindMedianIndex(range, src, rrange);
    const auto left = src.Sub(0, median);
    const auto right = src.Sub(median, src.size());

    auto ret = MedianCut(left, depth -1);
    const auto rhs = MedianCut(right, depth-1);
    ret.insert(ret.end(), rhs.begin(), rhs.end());

    return ret;
}

int
Find(std::vector<ExtractedColor>* psource, const Rgbi& color, float length)
{
    auto& source = *psource;

    for(int i=0; i<source.size(); i+=1)
    {
        if((rgb(source[i].color) - rgb(color)).GetLength()*255 < length)
        {
            return i;
        }
    }

    source.emplace_back(color, 0);
    return source.size() - 1;
}

std::vector<ExtractedColor>
ExtractColors(const std::vector<ImageAndFile>& images, float range)
{
    auto ret = std::vector<ExtractedColor>{};

    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto index = Find(&ret, rgbi(img.image.GetPixel(x,y)), range);
            ret[index].count += 1;
        }
    }

    return ret;
}

std::vector<ExtractedColor>
ExtractColors(const std::vector<ImageAndFile>& images)
{
    std::map<int, int> colors;
    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto color = rgbi(img.image.GetPixel(x, y));
            const auto hex = color.ToHex();
            auto val = std::get<0>(colors.try_emplace(hex, 0));
            val->second += 1;
        }
    }

    auto ret = std::vector<ExtractedColor>{};
    for(const auto c: colors)
    {
        ret.emplace_back(Rgbi::FromHex(c.first), c.second);
    }
    return ret;
}

std::vector<Rgbi>
ExtractAllColors(const std::vector<ImageAndFile>& images)
{
    auto ret = std::vector<Rgbi>{};
    for(const auto& img: images)
    {
        for(int y=0; y<img.image.GetHeight(); y+=1)
        for(int x=0; x<img.image.GetWidth(); x+=1)
        {
            const auto color = rgbi(img.image.GetPixel(x, y));
            ret.emplace_back(color);
        }
    }

    return ret;
}

bool
HandleImage
(
    const std::vector<std::string>& files,
    int depth,
    int image_size,
    const std::string& file
)
{
    // load images
    const auto images = LoadImages(files);
    if(images.empty())
    {
        return false;
    }

    const auto start = Now();

    // extract colors
    auto all_colors = ExtractAllColors(images);
    auto colors = MedianCut(SubVec{&all_colors}, depth);

    const auto end = Now();

    const auto seconds = SecondsBetween(start, end);
    std::cout << "Palette extraction took " << seconds << " seconds.\n";

    if(colors.empty())
    {
        return false;
    }

    Image image;
    image.SetupNoAlphaSupport(image_size * colors.size(), image_size);
    for
    (
        int i = 0;
        i < Csizet_to_int(colors.size());
        i += 1
    )
    {
        DrawSquare
        (
            &image,
            colors[i],
            image_size * i,
            image_size - 1,
            image_size
        );
    }

    io::ChunkToFile(image.Write(ImageWriteFormat::PNG), file);
    return true;
}

bool
HandlePrint
(
    const std::vector<std::string>& files,
    int depth
)
{
    // load images
    const auto images = LoadImages(files);
    if(images.empty())
    {
        return false;
    }

    // extract colors
    auto all_colors = ExtractAllColors(images);
    auto colors = MedianCut(SubVec{&all_colors}, depth);

    std::sort
    (
        colors.begin(),
        colors.end(),
        [](const Rgbi& lhs, const Rgbi& rhs) -> bool
        {
            return rgb(lhs).CalcLuminance() < rgb(rhs).CalcLuminance();
        }
    );

    if(colors.empty())
    {
        return false;
    }

    // print colors
    for(auto c: colors)
    {
        std::cout << c << "\n";
    }

    std::cout << "Found #colors: " << colors.size() << "\n";
    return true;
}

int
main(int argc, char* argv[])
{
    auto parser = argparse::Parser
    {
        "extract colors from images"
    };
    auto subs = parser.AddSubParsers();

    subs->Add
    (
        "print", "print all colors found in images",
        [](argparse::SubParser* sub)
        {
            std::vector<std::string> files;
            int depth = 3;

            sub->Add("--depth", &depth)
                .AllowBeforePositionals()
                .Nargs("R")
                .Help("change the palette depth")
                ;
            sub->AddVector("files", &files)
                .Nargs("F")
                .Help("the files to analyze")
                ;

            return sub->OnComplete([&]
            {
                const auto was_extracted = HandlePrint
                (
                    files, depth
                );

                return was_extracted
                    ? argparse::ParseResult::Ok
                    : argparse::ParseResult::Error
                    ;
            });
        }
    );

    subs->Add
    (
        "image", "write all colors found in images to a palette image",
        [](argparse::SubParser* sub)
        {
            int image_size = 5;
            std::string file = "pal.png";
            std::vector<std::string> files;
            int depth = 3;

            sub->Add("--depth", &depth)
                .AllowBeforePositionals()
                .Nargs("R")
                .Help("change the palette depth")
                ;
            sub->Add("--size", &image_size)
                .AllowBeforePositionals()
                .Nargs("S")
                .Help("change the image size")
                ;
            sub->Add("-o, --output", &file)
                .AllowBeforePositionals()
                .Nargs("FILE")
                .Help("change where to save the output")
                ;
            sub->AddVector("files", &files)
                .Nargs("F")
                .Help("the files to analyze")
                ;

            return sub->OnComplete([&]
            {
                const auto was_extracted = HandleImage
                (
                    files,
                    depth,
                    image_size,
                    file
                );

                return was_extracted
                    ? argparse::ParseResult::Ok
                    : argparse::ParseResult::Error
                    ;
            });
        }
    );

    return ParseFromMain(&parser, argc, argv);
}
<|endoftext|>


<commit_before><commit_msg>implement test for tdf#43700<commit_after><|endoftext|>


<commit_before><commit_msg>opencl-test.cxx: -Werror=unused-macros<commit_after><|endoftext|>