Странный случай с MemoryLayout с использованием протокола struct, сообщается другой размер - PullRequest
0 голосов
/ 12 февраля 2020

Я работаю над движком рисования, используя Металл. Я перерабатываю предыдущую версию, поэтому, начиная с нуля

Я получаю ошибку Выполнение буфера команд было прервано из-за ошибки во время выполнения. Вызванная ошибка зависания графического процессора (код IOAF 3)

После некоторой отладки я возложил вину на свою процедуру drawPrimitives, я нашел случай весьма интересным

У меня будут различные кисти, все они будут работать с указанием c информации о вершине

Итак, я сказал, почему бы и нет? Пусть все кисти отвечают на протокол

Протокол для вершин будет следующим:

protocol MetalVertice {}

И информация о вершине, используемая этим параметром c bru sh, будет:

struct PointVertex:MetalVertice{
    var pointId:UInt32
    let relativePosition:UInt32
}

bru sh может быть вызван либо путем предоставления ранее созданных вершин, либо путем вызова функции для создания этих вершин. В любом случае, реальное рисование происходит в функции вершины

var vertices:[PointVertex] = [PointVertex].init(repeating: PointVertex(pointId: 0,
                                                                               relativePosition: 0),
                                                        count: totalVertices)

        for (verticeIdx, pointIndex) in pointsIndices.enumerated(){
            vertices[verticeIdx].pointId = UInt32(pointIndex)
        }

 for vertice in vertices{
            print("size: \(MemoryLayout.size(ofValue: vertice))")
        }

        self.renderVertices(vertices: vertices,
                            forStroke: stroke,
                            inDrawing: drawing,
                            commandEncoder: commandEncoder)

        return vertices
    }


    func renderVertices(vertices: [MetalVertice], forStroke stroke: LFStroke, inDrawing drawing:LFDrawing, commandEncoder: MTLRenderCommandEncoder) {

        if vertices.count > 1{
        print("vertices a escribir: \(vertices.count)")
            print("stride: \(MemoryLayout<PointVertex>.stride)")
            print("size of array \(MemoryLayout.size(ofValue: vertices))")
            for vertice in vertices{
                print("ispointvertex: \(vertice is PointVertex)")
                print("size: \(MemoryLayout.size(ofValue: vertice))")
            }
        }
        let vertexBuffer = LFDrawing.device.makeBuffer(bytes: vertices,
                                                       length: MemoryLayout<PointVertex>.stride * vertices.count,
                                                       options: [])

. Это была проблема, вызов этого специфического c кода приводит к следующим результатам в консоли:

size: 8
size: 8
vertices a escribir: 2
stride: 8
size of array 8
ispointvertex: true
size: 40
ispointvertex: true
size: 40

В предыдущем функция, размер вершин составляет 8 байт, но по какой-то причине, когда они входят в следующую функцию, они превращаются в 40 байт, поэтому буфер неправильно создается

, если я изменяю сигнатуру функции на:

    func renderVertices(vertices: [PointVertex], forStroke stroke: LFStroke, inDrawing drawing:LFDrawing, commandEncoder: MTLRenderCommandEncoder) {

Вершины правильно указаны как 8-байтовые, и процедура рисования работает как задумано

Что-то я пропустил? если протокол MetalVertice вносит некоторый шум?

1 Ответ

1 голос
/ 12 февраля 2020

Чтобы выполнить требование, чтобы типы значений, соответствующие протоколам, могли выполнять динамическую диспетчеризацию c (а также частично, чтобы гарантировать, что контейнеры типов протоколов могут предполагать, что все их элементы имеют одинаковый размер) Swift использует так называемые экзистенциальные контейнеры для хранения данных типов значений, соответствующих протоколу, наряду с метаданными, указывающими на конкретные реализации каждого протокола. Если вы слышали термин таблица свидетелей протокола , вот что вам мешает.

Подробности этого вопроса выходят за рамки этого ответа, но вы можете проверить это видео и этот пост для получения дополнительной информации.

Мораль этой истории такова: не думайте, что Свифт выложит ваши структуры как написано. Swift может переупорядочивать элементы структуры и добавлять отступы или произвольные метаданные, и это практически не контролирует вас. Вместо этого объявите структуры, которые вы должны использовать в своем коде Metal, в файле C или Objective- C и импортируйте их через соединительный заголовок. Если вы хотите использовать протоколы, чтобы упростить полиморфное обращение к своим структурам, вы должны быть готовы скопировать их по элементам в свои обычные старые структуры C и быть готовы заплатить стоимость памяти, которую влечет за собой это удобство.

...